-
入门
- 变量常量
- 变量:指定的名称绑定特定的值
- 定义:第一次变量赋值的时候,在内存中创建一变量值,创建一份变量名,名绑定到值
- 修改变量绑定:先跟原来的值解绑,再绑定其他的值(忽略数值类型)
- x=y=z=3
- 平行赋值:x,y,z=1,2,3
- 变量赋值注意:
- 1 名字
- 2 变量使用前必须赋值
- 3 再变量赋值时可以使用不同的类型进行赋值
- 变量的删除
- del 变量名 注意:只删除变量名,不删除变量值
- 常量:习惯
- 指定变量名全部大写+数字:成为常量
- None----变量值的缺失
- 变量:指定的名称绑定特定的值
- 语言分类
- 动态语言:变量类型不固定,定义的时候不需要指定变量的类型
- 静态语言反之
- 强类型语言:定义一个变量后,指定了一种类型,如果不经过强制转换,那么就一直保持当前的类型
- 弱类型语言反之
- python是一种动态强类型语言
- 输入输出
- print(元素,seq,end)
- end默认"\"
- seq默认" "
- input("提示信息"),返回值是字符串
- print(元素,seq,end)
- 变量常量
-
基础
- 数据类型
- 1.整数(int)
- 定义 i = 整数
- 取值范围:以当前计算机的位数为准
注意2x区分int long3x 当超过32位的时候自动用long - 四个进制
- 手工转换:1下除法 2乘幂
- 二进制 bin() 0b
- 八进制 oct() 0o
- 十进制 int()
- 十六进制 hex() 0x
- 2.布尔类型(bool)
- true 1
- false 0
- 3.浮点类型(float)
- 定义 f=0.1
- 取值范围:有范围 sys.float_info_max(min)
- 科学计数法
- 特使的浮点数
- inf 正无穷或负无穷
- Nan 不是数字
自己跟自己也不相等可以使用math模块下的isnan方法判断一个变量是不是nan
- 浮点 数的不精确性
- 因为小数转化成二进制的时候,有的小数转换时是无限小数,只能存储近似值
- 注意
- 1.避免数量级相差较大的浮点数类型进行运算
- 2.避免等值判断
- 解决不精确性
decimal.Decimal(value) 默认保留28位value传承字符串,可以得到精确的值注意有的小数0.5 0.25.... 可以存储承精确的小数
- 4.复数类型(complex)
- 实部虚部都是浮点类型
- 5.转换
- 默认值
- int 0
- folat 0.0
- bool False
- complex 0j
- 注意:
- 负数类型不能转换成整型和浮点类型
- 其他类型转换成布尔类型:false 0 0.0 0j 其他时候全部为true
- 运算级别:复数类型>浮点类型>整型>布尔类型
低级别和高级别运算按照高级别来算
- 算术运算符
- + 加
- - 减
- * 乘
- / 除
- % 取余
- // 取整
- ** 求幂
- 赋值运算符
- = 赋值 x=25 y=10
- += 加赋值 x+=y ---- x=x+y 35
- -= 减赋值 x-=y ---- x=x-y 15
- *= 乘赋值 x*=y ---- x=x*y 250
- /= 除赋值 x/=y ---- x=x/y 2.5
- %= 取余数赋值 x%=y ---- x=x%y 5
- **= 幂赋值 x**=y ---- x=x**y 95367431640625
- //= 最整除赋值 x//=y ---- x=x//y 2
- 比较(关系)运算符 返回值为布尔类型
- > 大于
- < 小于
- == 等于
- != 不等于
- >= 大于等于
- <= 小于等于
- 逻辑运算符
- and 逻辑与
- or 逻辑或
- not 逻辑非
- 位运算符
- & 位与 对位都是1则为1,否则为0
- | 位或 对位都是0则为0,否则为1
- ^ 位异或 对位同0或同1为0,否则为1
- ~ 取反 对位0则为1,对位1则为0
- >> 左位移 操作数左移,最高位溢出,左补0
- << 右位移 操作符右移,最低位溢出,右补0
- 位移时,符号位不变,左移*2,右移/2/
- 1.整数(int)
- 序列
- 序列是一块用于存放多个值的连续内存空间,按一定顺序排列,每一个元素都分配一个数字,称为索引或者位置。
- python中,序列有:列表,元组,集合,字典和字符串
- 索引(index)
- 正数向右,负数向左
- 切片
- 访问序列中元素的一种方法
- 格式: 序列[start:end:step]
- start 开始的位置 默认值为0
- end 结束的位置
- step 步长 默认值为1
- 列表 list
可变序列- listname=[元素1,元素2,.......]
- 创建空列表:emptylist = []
- 列表相关操作
- 添加
- .append(obj) 将obj添加至列表末尾
- .insert(self,index,obj) 将obj插入index位置
- 修改
- 通过索引获取相关元素,再重新赋值
- 删除 del listname[index] 删除index对应的obj .remove(obj) 删除元素
- 获取指定元素出现的次数 .count(obj)
- 获取指定元素首次出现的下标 .index(obj)
- 原地排序 .sort()
- 添加
- 列表推导式
- 格式: [输出表达式 for i in 列表 if 条件]
- 字符串
- 拼接 str1+str2
- 计算长度 len(str)
- 切片 str[start:end:step]
- 分割字符串
- 检索
- count()检索单个字符出现的次数
- find()检索是否包含子字符串 存在则返回index 不存在返回-1
- index()同find()但不存在会报错
- startswith()用于检索字符串是否以指定子字符串开头
- endswith()用于检索字符串是否以指定子字符串结尾
- 字母大小写转换 lower()转成小写 upper()转成大写
- 去除空格和特殊字符strip()去除两侧的空格和特殊字符
- 元组 tuple
不可变序列- tuplename=(元素1,元素2,元素3......)
- 创建空元组 emptytuple=()
- 元组推导式 ---- 快速生成一个元组
- 格式:(输出表达式 for i in range(10)) ---生成一个10个元素的元组
- 流程控制
- 选择
- if elif else
- 循环
- for
- while
- 选择
- 数据类型
- 进阶
- 字典(dict)
- 定义:以键---值存储数据的数据类型
- 原理
- (1):为什么字典会加速检索效率
将key采用hash函数,映射成hashcode,hashcode对应value的地址 - (2):为什么字典的key是不可变类型
不可变类型才是可哈希的对象。对应一个对象只能产生一个hashcode(字符串,数值)
- (1):为什么字典会加速检索效率
- 格式:
- D={key1:value1,key2:valu2....}
- D=dict{key1=value1,key2=value2}
- 空字典={}
- 原理
- 操作
- 字典的key和value的访问和修改
- 访问:字典[key] 如果不存在会报错
- 修改:字典[key]=value 如果key不存在会添加一个键值对,如果存在则修改对应的value
- 如果在字典定义的时候,就有重复的key,value一定会使用最后一次赋值
- 支持 is in ==
- 字典的key和value的访问和修改
- 方法
- (1)fromkeys:创建同值不同键的字典
- (2)setdefault:追加字典的减值
如果不指定value,则最佳value为None的键值对如果key存在,则不进行追加 - (3)update:追加字典,多个键值对
- (4)pop(key):删除指定的键值对, return value
- (5)popitem():随机删除键值对,return value
- (6)clear():清空字典
- (7)get(key,errinfo):获取key对应的value
如果key不存在不会报错,可以指定错误信息 - (8)copy():浅拷贝
- 遍历
- Keys
- Values
- Items
- 字典推导式
- {输出表达式 for 键,值 in 字典}
- 定义:以键---值存储数据的数据类型
- 集合(set)
集合的顶层是字典实现的,其实就是value为none的字典,是字典的key组成- 定义
- A={不可变类型的元素,......}
- 注意空set 不能是{}----这是空字典
- 空set ---> set()
- 字典的长度:Len(set)
- 特点
- (1)集合的元素不重复
- (2)无序
- (3)集合的元素是不可变类型
- 操作
集合不能使用任何跟索引相关的方法获取集合元素- 操作符:in is == (父集)>(子集) < -(差集) &(交集) |(并集) ^(相对差集)
- 方法
- (1)add:如果重复,则不添加
- (2)remove:删除指定元素,如果不存在,报错
- (3)discard:删除指定元素,不存在不会报错
- (4)pop:随机删除元素,返回删除元素
- (5)copy:对于集合来说深拷贝浅拷贝一样
- (6)difference差集
- (7)intersection交集
- (8)union交集
- (9)symmetric相对差集
- 以上带yudate的都是原地修改
- 遍历
- for i in 集合:
- 集合推导式
- {输出表达式 for i in 集合}
- 定义
- 数据转换
- list() 最常用
- tuple()
- set() 去重
- dict()
- 函数:具有一个功能或多个功能的代码块
- 函数的定义
- 格式
def 函数名(参数) 函数体 返回值 - 意义:实现一个或者多个代码的代码块
- 格式
- 参数
- 1.位置参数:按位置传入参数
- 2.默认参数:默认值一定是不可变类型
- 3.命名关键字参数:*,命名关键字参数
程序的可读性好传入参数的顺序忽略如果有默认参数,简化调用 - 4.可变参数(收集参数):将位置参数打包成元组,传给形式参数
- 定义 : * 打包
- 调用 : * 拆包
- 5.关键字参数 :将命名关键字参数打包成字典,传递给形式参数
- 定义 : ** 打包
- 调用 : ** 拆包
- 位置参数>命名关键字参数/可变参数>关键字参数
- 万能参数: *args,**kwargs
- 返回值
- return :函数执行之后的结果
- 返回值为None
(1):return None(2):return(3):没有return - return返回值最好使用元组返回
- 可以使用多个参数接函数的多个返回值,一一对应
- 程序执行到return,就不在往下执行
- 返回值为None
- return :函数执行之后的结果
- 值传递
- 可变类型的值传递
- 当调用函数后,会将可变类型的对象的值改变
- 不可变类型的值传递
- 当调用函数后,不会改变不可变类型的值
- 可变类型的值传递
- 命名空间和作用域LEGB原则
- 命名空间
- 存储所有的变量名,函数名,类名,模块名 ......
- 格式:键值对(字典) 名字---对象的内存地址
- 分类:
- 内建命名空间:python解释器启动的时候创建
- 全局命名空间:再读取py文件的时候创建,当程序执行完毕后销毁
- 局部命名空间:再函数定义的时候创建,再函数执行完毕的时候,销毁
- 命名空间之分大小,不分包含关系
- 作用域:再命名空间中的名字在那个区域起作用
- 内建命名空间:所有的py
- 全局命名空间:当前的py,如果其他py也想使用,需要导入
- 局部命名空间:再当前的函数中
- 作用域也之分大小,不分包含关系
- LEGB原则
L---局部作用域E---外围作用域G---全局作用域B---内建作用域- 访问:按照LEGB原则,如果找不到会报错(Nameerror)
- 修改:再当前命名空间检索,如果找不到,不会向上继续找,而在当前命名空间新创建命名
- 如果需要修改全局命名空间或者外围命名空间的变量,可以通过global和noglobal
- 删除:只支持当前命名空间的名称删除
- 命名空间
- lambda表达式(匿名函数)
- 格式:lambda 参数 : 返回值表达式
- 递归
- 递推和回归
- 思路:主要去找突破点
- 最大递归深度,不仅仅是调用自身的次数,还包含调用其他函数的次数
- 文档和注释
- 文档:""" """
- 函数名.__doc__
- Help(函数名)
- 注释:
- 参数:参数的类型
- 返回值->返回值的类型
- 函数名.__annotations
- 文档:""" """
- 函数的定义
- 类和对象
- 魔法方法
- __new__ : 在创建对象的时候执行
- __init__ : 在__new__执行之后对对象进行初始化
- __del__ : 在对象销毁的时候自动执行
- __str__ : 当调用内建函数str() format() print()时会自动调用的方法,返回值为str
- __repr__ : 如果方法中没有定义str,却需要调用str,默认调用repr
- __bytes__ : 返回字节
- 动态属性操作
- 1.hasattr(obj,name):判断obj中是否存在(name)指定的属性名
- 2.setattr(obj,name,value):将obj中的name设置成value
- 3.getattr(obj,name):将obj对象中的name属性值取出----给对象动态赋值
- delattr(obj,name):删除obj中的name属性
- 魔法方法
- 面向对象
- 面向过程:程序里面有大量的函数,每一个环节都是进行函数的调用
先分析怎么做,再分析是谁 - 面向对象:根据需求划分不同的对象,将对象划分成若干个类,以对象的方式进行方法或者行为来调用,不是以函数为单位,而是以对象为单位
先分析是谁,再分析怎么做
- 面向过程:程序里面有大量的函数,每一个环节都是进行函数的调用
- 面向对象的三个特征: 封装、继承、多态
- 封装
信息的隐藏,方便调用者调用,数据安全性- 成员的私有化
一般情况下私有实例属性和实例方法- 成员:实例属性、实例方法、类属性、类方法、静态方法
经常对实例属性和实例方法进行封装(对成员进行私有化) - 目的:只能在当前类中使用和访问,不能再类的外部直接访问
- 方法
- 1.如果希望一个属性或方法是私有的: __名字 (不以两个__结尾)
- 如何在类的外部访问私有属性:在类的里面提供get()和set()方法
- 2.使用proprety完成成员的私有化
- 目的:为了方便调用者调用
- (1)对外界访问的名字=proprety(get方法,set方法,del,"注释")
在定义好get方法和set方法之后,再进行封装,使调用者更容易调用- 例如:先定义get方法和set方法
- 封装
- 使用
- (2)proprety装饰器
- get方法:前面加 @proprety
- set方法:前面加 @proprety.setter
- del方法:前面加 @proprety.deleter
- 1.如果希望一个属性或方法是私有的: __名字 (不以两个__结尾)
- 优势
提高代码的扩展性和可维护性 - 关于python语言封装:假封装
只是将__名字伪装成:_类名+私有成员的名字
- 成员:实例属性、实例方法、类属性、类方法、静态方法
- 成员的私有化
- 继承
- 需求:使用继承解决代码中大量重复的属性、方法
- 继承的关系
一般与继承的关系【特殊类】 是 【一般类】的 子类【一般类】 是 【特殊类】的 父类- 子类可以继承父类,继承父类后继承父类下的所有的成员(属性,方法),作用跟定义在自己类中一样
- 继承的方式
- 1.显式继承
class 子类(父类): 类体 - 2.隐式继承
在python中,如果没有写明()继承,就叫隐式继承只有一种情况,继承object父类所有类都会直接或者间接继承object父类
- 1.显式继承
- 继承的意义
- 在父类上扩展功能
当父类有一些属性和方法允许相关的子类使用的时候,子类可以直接使用,如果子类有特殊的属性和方法,可以自行定义和扩展,重写。 - 实现了代码的重用
通过继承实现代码的重用性,可以将公共的部分提取出来,放在父类中。 - 解决实际编程问题时:对象---抽取---子类,子类---抽取---父类
- 在父类上扩展功能
- 两个重要的内建函数
- 1.isInstance(对象,类型):判断第一个参数指定的对象是否属于第二个参数的类型 (父类也可以)
子类对象属于子类类型子类类型属于父类类型父类类型属于object类(任何类都属于object类)子类对象-->子类-->父类-->object - 2.issubclass(类型,类型):第一个参数是否是第二个参数的子类 (自己也是自己的子类)
- 1.isInstance(对象,类型):判断第一个参数指定的对象是否属于第二个参数的类型 (父类也可以)
- 成员的继承
- 类属性,实例属性,类方法,实例方法,静态方法
- (1)类属性,类方法,静态方法,实例方法
- 关于方法的重写
子类继承父类,父类的成员未必完全适合子类,可以重写来自行扩展方法- 规则:如果子类中重写了父类的方法,运行子类方法,没有则运行父类方法(不能部分继承)
- 重写后调用父类成员方法:super().父类的成员 进行访问
- 关于方法的重写
- (2)实例属性的继承
- 如果子类中没有定义__init__方法,就会执行父类中的init方法,进行初始化
- 如果子类中定义了__init__方法,就会按照子类中的属性进行初始化
- 如果子类的init中没有继承父类的init,pycharm会提示继承关系是否有问题
- 如果子类的init方法继承了父类的init,尽量将父类的init放在第一行
- (4)多重继承
- 一个子类可以继承多个父类
- 多重继承涉及继承的顺序,按照从左到右的顺序,同名方法,哪一个父类在前面,就先继承哪一个父类的方法
- MRO原则:方法调用顺序
- 如果是单继承,搜索顺序是从子类到父类
- 如果是多继承,子类到每一个父类都是一条分支,按照继承的顺序,沿着每一条分支,从子类到父类进行搜索,知道object(深度优先)
- 如果非要继承指定父类的方法
- (1)将指定父类放在前面
- (2)指定类.方法
- 将代码规范化
- 在父类中def 方法(self): pass 子类都需要重写 方法
- 多态
运行或者编译时的多种形态- python是动态强类型语言,在多态上体现的不明显。
- 封装
- 模块和包
- 模块(module)
物理上:一个文件就是一个模块逻辑上:模块(功能划分)- 基本概念
- 模块中可定义:类,函数,变量
- 模块的名字就是文件的名字,被存储在模块命名空间
- 划分模块的好处
- (1)有利于项目按照功能进行划分,每个人负责一个或多个模块,方便协作开发
- (2)模块提供了独立的命名空间(全局命名空间),解决命名冲突问题
- (3)模块可以提供多人使用,提高程序的复用性
- 在python中一个模块可以支持多个class 的建立
- 模块的别名
当导入模块的时候,可以用as为当如的模块名进行重命名- 格式:(1) import 模块名 as 模块别名
- (2)form 模块名 import 名称 as 名字别名
- 一旦重命名,原名就不可使用
- 好处:
- (1)可以解决当前模块和引用模块的命名冲突问题
- (2)如果导入模块的名字很长,可以起到简化作用
- 隐藏模块的数据
只对 from import * 又用,对import 不起作用- 两种方式:
- (1)标明不能被导入的名字
- 方法:名字前面_ 例如 _w
如果直接写明from 模块 impot 变量名 ---不受不能导入限制
- 方法:名字前面_ 例如 _w
- (2)标明能被导入的名字
- 方法:__all__=["能够导入的名字","。。"]
如果直接写明from 模块 impot 变量名 ---不受不能导入限制
- 方法:__all__=["能够导入的名字","。。"]
- 如果一个变量即被标注_w,同时又被加入到__all__列表里,能被导入
- 原因:导入时,先检索__all__列表下的所有变量,然后再去检索出了_意外的变量
- (1)标明不能被导入的名字
- 两种方式:
- __name__ : 主方法
- 能够返回当前模块的名称
- 一个模块一个py文件运行的时候有两种方式:
- 脚本执行(文件式),直接run-----__name__==__main__
- 通过其他模块import当前模块----__name__==模块的真实名字
- 模块的搜索路径
- 搜索顺序:
- (1)内建解释器
- (2)作为脚本执行模块的所在路径
- (3)python的环境变量路径
- (4)python的安装路径(lib包下)
- 注意:模块起名字不要起lib包下的名字,也不要去用内建解释器下的名字
- 模块的使用 (1) import (2)from.....ipmort
当导入模块后,计算机会将该模块中所有的语句都编译执行但是只在第一次导入的时候执行,不会重复导入在当前模块中定义的变量或者函数不会跟模块中的变量或者函数名冲突- import
- 格式:import 模块名(多个模块用,分隔)
- 调用模块方法:模块名.模块方法
- from......import:可以选择性导入模块中的属性或者方法
和import一样在导入时也会执行该模块下的所有代码使用form.....import模式可能会造成名字冲突,后定义为准- 格式:from 模块名 import 属性,方法,类(多个用,分隔)
- 使用时直接使用import进来的名字即可
- dir函数,返回指定对象相关绑定的名字,如果没有参数,会返回当前作用域下的名字
- import
- 模块的缓存
(1)缓存字节码文件只是提高模块的加载速度,并不是提高模块的运行速度(2)只有自定义的模块才能产生字节码文件(3)字节码文件都可以脱离源文件执行(跨平台)- 导入模块时,会产生一个缓存文件.pyc,字节码文件,预编译文件
- 产生的时间:自定义的py模块,在被导入的时候创建
- 产生的位置:当前的文件路径下创建__pycache__包
- 基本概念
- 包(package)
- 基本概念
- 包提供了更加独立的命名空间,一个包可以包含多个模块,也可以包含多个子包
- 作用
- (1)提供模块的分类管理
- (2)提供了独立命名空间,解决模块重名问题
- 导入包
跟模块几乎一样- improt 包名.模块名
- from 包名 import 模块名
- from 包名. 模块名 import 变量名,类名,函数名
- __init__.py文件
init文件式每个python的开发包必须存在,否则python解释器会把当前的文件认为是普通目录,不会执行- __init__可以是空的,也可以用来进行初始化,再__init__中定义的名称都是全局作用域的
- 引用的时候,[包名].[名称]引用。名称被认为是包的属性
- 在init下定义的变量,在整个包的所有py模块中都可以访问到
- init文件,当被import包的时候,会执行init中的所有文件
- 如果希望使用init中的变量,必须导入模块名
- __all__变量
- 在init文件中可以使用__all__控制使用from....import * 方式导入的名称
- 基本概念
- 常用模块
- 数学模块(math)
- 圆周率 pi
- e e
- 向上取整 math.ceil(x)
- 向下取整 math.floor(x)
- 返回x的y次幂 math.pow(x,y)
- 返回float的绝对值 math.fabs(x)
- x得阶乘 math.factorial(x)
- 取余 math.fmod(x,y) 向0取整
- x,y,z累加和 math.fsum([x,y,z])
- x和y的最大公约数 math.gcd(x,y)
- 返回x的平方根 math.sqrt(x)
- random模块(随机)
- 产生0-1之间的小数 包括0 不包括1 random.random()
- 产生a-b之间的整数 包括a,b random.randint(a,b)
- 随机产生a-b之间的数,包含a,不包含b,可以定义step步长 random.randrange(a,b,step)
- 返回a-b之间的浮点数,包含a,b random.uniform(a,b)
- 从seq中随机选择一个元素 random.choice(seq)
- 从iterable中随机取出k个不同的元素 random.sample(iterable,k)
- 就地洗牌 random.shuffle(seq)
- 时间
- 1.time模块
- 返回UTC(本初子午线)时间相差的秒数 time.timezone
- 返回从新纪元时间到当前时间走过的秒数 time.time()
- 返回本地时间 time.localtime(s):参数是秒
- 返回的是timetuple到新纪元走过的秒数 time.mktime(timetuple)
- 返回时间元组对应的str类型 time.asctime(timetuple)
- 返回从新纪元+s秒 time.ctime(s)
- 使当前程序暂停执行s秒 time.sleep(s)
- 返回计算时间,用来计算经历时间长度 time.perf_counter()
- 将时间元组转成字符串(按指定的格式) time.strtime(格式,时间元祖)
%Y 年份(四位数) %y年份(两位数)%m 月份 %d 日 %H 24小时制 %I 12小时制%M 分钟 %S 秒 - 返回字符串指定时间的元组 time.strptime(字符串,格式)
- datetime模块: date time datetime 三个类
- date
- date
- 1.time模块
- 数学模块(math)
- 模块(module)
- 异常
一个程序异常处理能力,也是评价程序好坏的标准,涉及程序的稳定性- 概念
- 错误:编译期,不能被捕获
- 异常:运行期:程序没有按照正常或者期望方式执行,可以被捕获
- 异常的传播方式:
- 在异常发生的位置,创建异常类型的对象,“暂停”,程序会从程序的上下文中寻找有没有异常处理的程序,有则进入异常处理程序(try except),没有则向上传播
- 向上传播:
- 如果异常时在函数中产生的,会传播给函数的调用者
- 如果异常是在模块的顶级位置产生,会传播给引用这个模块的模块
- 向上传播:
- 在异常发生的位置,创建异常类型的对象,“暂停”,程序会从程序的上下文中寻找有没有异常处理的程序,有则进入异常处理程序(try except),没有则向上传播
- 异常的常见类型
- 两个常见异常类
- BaseException:所有异常类的父类 基类
- Exception:是BaseException的子类 处理普遍异常的相关代码
- 1.ZeroDivisionError 除数为0的异常
- 2.NameError 名字错误异常:命名空间中没有对应名字
- 3.TypeError 类型不匹配:引用的数据类型不一致
- 4.AttribufeError 属性异常
- 5.IdentationError 缩进异常(错误)
- 6.IndexError 索引异常,再序列类型的数据类型中产生索引越界异常
- 7.UnboundLocalErrpr 局部变量访问异常
- 8.AssertionError 断言异常
- 9.ImportError 模块不存在异常
- 10.KeyError 字典中的key不存在异常
- 11.RecursionError 递归异常
- 12.SyntaxError 语法错误异常
- 13.StopIteration 迭代终止异常
- 14.valueError 数值异常
- 两个常见异常类
- 捕获异常 try----except
注意:异常处理的时候,一般都是在调用端进行捕捉异常不能捕捉到的异常都属于编译期出现的错误能够捕捉到的都是运行期的异常- 格式
try: 可能产生异常的程序except 异常的类型1: 处理异常的程序except 异常的类型2: 处理异常的程序except 异常的类型3: 处理异常的程序 - 捕捉异常出现的三种情况:
- 1.程序运行过程中,没有出现任何异常,程序会正常运行到结束,不会走except
- 2.如果在try中出现了异常,简则在except中有没有符合自己类型的异常,如果有,则进入相关except处理,会继续正常秩序try异常处理以外的其他代码(如果有多个except,只会走一个符合的except)
- 3.如果try中发生异常,但是except没有合适的异常类型进行匹配,异常继续向上传播
- 异常的合并:当异常类型一致,符合业务要求,可以对异常进行合并
- except (异常类型1,异常类型2,.......)
- 如果在开发中无法判断出现的异常,可以使用except不表明异常类型,可以捕捉所有异常类型(慎用,会隐藏所有异常)
- 注意:捕获多个异常需要注意异常的顺序
如果异常类型之间没有继承关系,except分支的顺序并不重要如果异常类型之间存在继承关系,需要将父类异常放在子类异常后面
- 格式
- 异常中的finally和else
try: 可能出现异常的代码excepy 类型: 处理异常else: 子句:当没有抛出异常的时候,才会执行的语句 (当有异常抛出的时候,就不执行的代码段) else不是不可替代,只是让代码更清晰finally: 子句:无论有没有抛出异常都会执行的语句 应用场合:释放资源时常用 - 强制抛出异常
- 关键字raise 异常类型("异常的信息")
- 用法
- (1)except中使用raise
处理并且继续向上抛异常 - (2)直接使用rasie抛出异常
常用于提示
- (1)except中使用raise
- 自定义异常
- 自定义异常的格式
必须继承Exception父类重写__init__
- 自定义异常的格式
- 概念
- 字典(dict)
-
目录
高级
- 迭代器 生成器 装饰器
- 迭代器 iterrator
容器类:能够存放多个元素的数据类型序列:字符串,列表,元组,字节字典 集合- 可迭代对象:可以进行遍历的对象 (基类:iterable)
- 可迭代对象都是Iterable扩展类(子类)
- 重写了__iter__(self) 可以返回一个【迭代器】
- 说明:不是所有的可迭代对象都实现了__iter__方法,有的实现__getitem__
- 迭代器 (基类:iterator)
迭代器是用来遍历对象的(迭代器下有next)- 迭代器是迭代对象的特殊形式
返回true迭代器是迭代对象的子类 - iter方法:返回迭代器
- next方法:返回下一个元素
- 注意:
- 迭代器通过next遍历元素时,如果迭代器为空,会返回stioineration异常
- 迭代器产生之后是一次性的对象,迭代元素被next遍历之后,就没有了,如果需要再次遍历,只能再重新获得迭代对象,继续next
- for循环底层一定是每次都从迭代对象中 ,重新获得一个迭代器,再进行遍历
- 迭代器是迭代对象的特殊形式
- 自定义迭代类型
- 满足抽象基础类规范
- 迭代对象:实现iter方法,返回一个迭代器
- 迭代器:实现next方法,返回下一个元素
- 迭代器和迭代对象一起使用,而不是单纯的使用迭代器
- 迭代器:只能使用一次
- 迭代对象:用来产生迭代器,又是可以构造复杂的数据类型
- 迭代器的缺点
- 实现一个迭代器,需要实现iter next方法
- 如果不使用for循环,需要自己捕捉stopiteration异常
- 一次性将数据全部迭代出来,结果不一定全部都使用,当数量级太大,会占用太多的内存 解决方案:生成器
- 可迭代对象:可以进行遍历的对象 (基类:iterable)
- 生成器 generator
python2.5之后出现的一个可迭代对象底层实现使用迭代器实现- 基本概念
- 生成器:给一个函数生成一个懒加载(按需加载)的迭代器
- 特征:生成器不会一次性计算出存储的所有数据,而是根据需求,一次仅计算一个数据
- 生成器的实现
- 生成器的数据可以通过两种方式获得:(1) next /send (2) for循环遍历
- (1)生成器表达式
元组推导式==生成器- 格式: x= (输出表达式 for i in 对象)
- print(next(x))
- (2)生成器函数
- 带有yield关键字的函数,就是生成器函数
- yield:能够让程序暂停到yield的位置,产出值
- 生成器函数调用的时候,不是直接执行,而是创建了一个生成器对象
- 生成器函数和普通函数的区别:
- 生成器函数会包含一个或者多个yield
- 被调用的时候,不会直接执行,而是返回一个生成器对象
- 可以通过next方法获得内部元素,也可以通过for遍历
- 当函数终止时,再次获得元素会报stopiteration异常
- 函数一旦执行遇到yield,函数执行会被暂停,将控制权交给调用者
- yield表达式
- 第一次调用生成器:产生生成器对象
- next:激活生成器
- 生成器表达式的结果就是,调用生成器send方法传入的参数
- 激活生成器:两种方式
- next()
- g.send(None)
- 获取生成器中的元素
- next(g)
- g.send("yield表达式的值value")
- 使用生成器编程的思路
- (1)抓住主线
- (2)再主线中插入生成器调用
- 基本概念
- 装饰器 decorator
- AOP面向切面的编程:将公用的模块提取出来,形成一个单独的组件
- 闭包
- 在内部函数中访问外部函数的变量,外部函数中直接返回内部函数名的调用
(.__closure__)可查看闭包情况 - 闭包的条件:
- 1.嵌套函数
- 2.内部函数能够使用范围外围变量
- 3.外部函数return内部函数的名字
def fun(); def fun1(); pass return fun1
- 闭包使用的场景:
- 1.函数中有一些变量在被调用后,任然希望保留
- 2.当函数需要扩展功能,又不能修改原来函数的名字
- 在内部函数中访问外部函数的变量,外部函数中直接返回内部函数名的调用
- 装饰器
- 装饰器是用来处理被装饰的函数,返回函数的函数名字,采用闭包的思想。在不修改原来函数的基础上,对现有函数进行扩充,功能的扩展
- 装饰器:@装饰的名字 语法糖
- 装饰器原理
- 装饰器底层是使用闭包实现的
- 装饰器的优化
- (1)参数的优化
- 使用万能参数*args,**kwargs
- (2)返回值
- 在inner中直接获取函数返回值,作为inner的返回值即可
def fun(); def fun1(); pass return fun1
- 在inner中直接获取函数返回值,作为inner的返回值即可
- (1)参数的优化
- 装饰器的叠加
- 使用装饰器扩展更多的功能---在装饰器上面再加装饰器
......@d2@d1def fun()
- 使用装饰器扩展更多的功能---在装饰器上面再加装饰器
- 含有参数的装饰器
- 如果在装饰器需要使用带其他的参数,name可以采用在外则包一层外部函数解决,在装饰原函数的时候,@装饰器(参数)
- 注意:不要在装饰器的一层传入其他的参数,装饰器的一层只传函数名
- 保留函数的元信息
- 使用functools下的wraps实现元信息的保留
- 类装饰器
- 实现装饰器的效果不仅仅可以使用装饰器函数,还可以使用类解决
- __call__:能够让对象通过方法去调用 相当于执行 对象()
- 迭代器 iterrator
- 文件
操作系统的文件分为:(1)文本文件 .txt(2)二进制文件 .mps .wmv .doc- 获取文件对象
- 格式:open("file","mode")
- file:泛指文件和文件夹的路径
- 相对路径
- 绝对路径
- mode: 同一等级模式不能同时使用
- (1)r读模式 w写模式 a追加模式 ----默认值r
r 指针在文件开头 w,a 指针在文件末尾 - (2)t文本(字符串)模式 b二进制(字节)模式 ----默认值t
- (3)+读写或写入
- (1)r读模式 w写模式 a追加模式 ----默认值r
- 关闭文件
- (1)直接关闭
- (2)try finally中
- (3)with打开和关闭 推荐
- 使用with打开文件 with open() as 文件对象名,...
- 文件的读取
文件对象是迭代器,当读取到末尾时,无法再读取任何内容,返回空- read(size):size不写或者-1代表将文件内容读出,size>0 读出对应的字节
- readline():返回文件的一行,保留原文档中的\n
- readlines():返回一个列表,每一个元素都是文件中的一行
- 通常如果文件过大,我们使用for循环的方式对文件对象进行迭代
- 文件的写入
- (1)write(content)方法
- (2)writelines(lines):将lines列表中的内容写入文件
- 文件的定位
- 文件的指针
- (1)当r模式的时候,指针在文件开头
- (2)w,a模式的时候,指针在文件的末尾
- (1)tell():返回指针的位置,即下一个要读取或者写入字节的位置
- (2)seek(offset,whence):改变指针位置
- offset:指定偏移量
- whence:指定相对位置:
- whence=0----文件头开始计算
- whence=1----文件当前的位置
- whence=2----文件尾开始计算
- 对于whence=0的时候,可以指定b,t模式
- 对于whence=1/2的时候,只支持b模式
- 文件的指针
- 文件与路径的操作
- 1.os.
- (1)mkdir():父目录必须存在,被创建的目录必须不存在,否则报错
- (2)makedirs():父目录可以不存在,会同时创建父目录
- (3)rmdir():只能删除空目录
- (4)removedirs():删除空目录,还会查看父目录是否为空,如果为空,则删除父目录,知道文件夹不为空
- (5)remove():删除文件,如果文件不存在不会报错
- (6)rename(原文件名,新文件名):重命名文件或目录 目录级别要一致
- (7)renames():重命名文件,可以应用不同的路径--相当于把原来路径下的文件删掉,在新目录下创建新文件
- (8)getcwd():返回当前工作目录
- (9)walk():遍历路径下的文件 返回一个元组
- dirpath:string 代表目录的路径,起始路径
- dirnames:list 包含dirpath下的所有子目录名字
- filenames:list 包含非目录文件的文件名
- (10)listdir():显示参数路径下的第一层名字
- 2.os.path.
- (1)abspath():返回绝对路径
- (2)basepath():返回路径中最后面部分,以/分隔符为准(只是字符串的处理,忽略路径是否真实存在)
- (3)commonpath():返回列表中所有路径的最大公共路径
- (4)exist():判断路径是否存在
- (5)返回目录或者文件的最后访问时间,修改时间
- getatime():访问时间
- getmtime():修改时间
- (6)getsize():获得文件的大小 字节为单位
- (7)isdir():判断是否存在目录或者文件
- (8)join():路径的拼接----绝对路径前的元素去掉,空元素会去掉
- (9)split():拆路径:dirname和basename
- shutil.
- (1)copy():复制
- (2)copy2():把元信息都复制
- (3)copytree():复制目录,连同子目录和文件都复制
- 1.os.
- 获取文件对象
-
数据结构
- 常用的数据结构
- 数组
- 链表
- 堆栈
- 队列
- 树
- 哈希表
- 将一组关键字映射到有限的函数,这种就叫做哈希散列表。
- 时间复杂度
- (1)O(1)
- (2)O(n)
- (3)O(n^2)
- (4)O(logn)
- O(1) < O(logn) < O(n) < O(n^2)
- 空间复杂度
- 用空间为代价,换取时间
- 查找
- 顺序查找:从头到尾
- 折半查找:每次查找中心点,通过判断大小,来进行重新获取start和and ----只适合排好序的列表
- 排序
- 冒泡排序(最重要)
- 俩俩比较(i,i+1)
- 外循环界限:n-1
- 内循环界限:n-1-i
- 时间复杂度:最优O(N) O(N^2)
- 稳定性:稳定
- 选择排序
- 每次选择一个最大(小)的元素,放到前面排序
- 时间复杂度:O(n^2)
- 稳定性:不稳定
- 插入排序
- 从第二个元素开始,插入到现有的排好序的数组中,保证每个元素插入之后都是有序数组
- 时间复杂度:最优O(n) O(n^2)
- 稳定性:稳定
- 希尔排序
- 插入排序的升级,最小增量排序,需要指定增量,将记录分组,对每一组数据进行插入排序
- 时间复杂度:不定
- 稳定性:不稳定
- 快速排序
- 递归的执行查找中心点,将比中心点小的元素放左侧,比中心点打的放右侧
- 时间复杂度:O(nlogn)
- 归并排序
- 分久必合合久必分
- 时间复杂度:O(logn)
- 稳定性:稳定
- 冒泡排序(最重要)
- 常用的数据结构
- 线程
- 基本概念
- 程序:程序可以理解成是一系列的指令集,程序是静态的
- 进程:当程序运行时,会创建一个进程
- 线程:进程基本执行单元,一个进程至少有一个线程
- 进程和线程之间的关系:
- 一个线程只属于一个进程
- 一个进程可以包含很多线程,只有一个主线程
- 进程资源、线程资源对比:
- 进程具有独立的空间和系统资源
- 线程没有独立的空间和系统资源,同一个进程下的多个线程共享该进程中的资源
- 问题:多线程中对于共享资源修改的问题-----多线程同步问题----线程不安全
- 多线程
- 单核cpu下:宏观并行,微观上任然是串行
- 多核cpu:真正并行
- 计算机执行----cpu执行,io操作
- 场合:
- 计算密集型:适合多核cpu的多线程
- io密集型:适合单核cpu的多线程
- 多线程的缺点:
- (1)线程本身也是程序 ,也是需要占内存,线程越多,占用的内存就越多
- (2)多线程之间的调用需要协调管理,需要cpu进行线程的跟踪
- (3)重要:多线程之间对共享资源的访问有影响,必须解决线程共享资源的问题,否则会导致数据的不一致
- 线程的创建
- 三种方式:
- (1)使用threading模块创建,通过target和args参数,创建线程
- target函数的名字
- args函数的参数必须使用元组传递
- .start()激活线程,不是执行线程,把当前线程拷贝到cpu的任务执行列表当中,等待cpu分配时间片执行
- (2)使用Thread类,重写run方法
- 当需要创建很多个执行相同方法的线程对象时,建议使用继承thread类,重写run方法
- run方法才是真正执行任务函数的方法
- 问题:start方法和run方法有什么区别?
- start:激活线程,不代表真正的执行,使得线程处于就绪状态,加入cpu执行任务列表
- run:真正执行任务的函数
- (3)使用线程池(高阶)
- (1)使用threading模块创建,通过target和args参数,创建线程
- 三种方式:
- 线程的生命周期
- 1.新建(人的出生):创建线程对象,没有执行能力
- 2.就绪(等待就业):调用了start方法,不是马上执行,把执行权力交给cpu
- 3.运行(入职工作):执行线程的任务,获得了cpu的时间片,cpu再一个线程运行的时候,是可能将时间片分配给其他线程
- 4.阻塞(生病):处于的等待的过程中,cpu不会将时间片分配给阻塞状态的线程
- 5.死亡(死亡):run方法执行完毕,或者run方法中抛出没有捕获的异常
- 线程的相关操作
- 1.threading.active_count():计算处于活跃状态线程的数量
活跃:在调用start之后(就绪),处于死亡之前。包含就绪、运行、阻塞 - 2.threading.enumerate():返回一个list,包含正在运行的线程
- 3.threading.get_ident():获得线程的标志,不同的线程标志不一样,唯一标志
- 4.threading.main_thread():返回执行解释器的主线程
- 5.threading.current_thread():获得当前执行的线程
- 6.start():启动线程,就绪状态
- 7.run():运行线程内容
- 8.join(参数):抢占时间片
- A线程中 B.join():在A线程中插入B线程,B线程必须执行完毕,才能继续执行A线程
- 参数:如果不写参数,会一直抢占时间片,直到线程执行完毕,有参数,参数代表等待时间(秒)
- 9. name():给线程设置属性,私有属性,被property化的私有属性
- 10. indet():线程标志 属性,get_indet()方法一样,线程标识符
- 11. is_alive():判断线程是否存活
- 12. daemon():判断是否是守护线程(后台线程)
- 默认的线程都是非守护线程(前台线程)
- 守护线程:守护线程先完成会等待线程执行完毕,再完毕
- 设置守护线程: .setdaemon(true):
- 注意:设置守护线程必须在start之前
- 1.threading.active_count():计算处于活跃状态线程的数量
- 线程的同步
- 在同一个进程下面,共享很多线程,线程之间对于资源上是共享的,共享会引发线程不安全的问题
- 问题:买票时,票只剩一张,同时两个人买
- 解决:同步:不要理解成同时执行,计算机中指排队执行
- 使用同步锁
- 在同一个时间点内,一个共享资源只能被一个线程方法
- threading中提供了锁的方法
- threading.lock()获取锁对象
- (1)lock.acquire() 加锁
- (2)lock.release() 解锁
- 死锁
- 死锁的定义:当两个或者多个线程同时拥有自己的资源,而此时,互相等待获得对方的资源
- 什么时候会出现死锁:编程的时候,处理并发操作,使用锁解决共享资源的问题的时候,会造成死锁
- 同步
- 通知和等待
- 对象=threading.Condition类下
- 对象.wait():等待,释放当前线程占用的锁
- 对象.notify():任选一个等待线程
- 对象.notifyall():通知所有等待的线程
- 通知和等待
- 线程队列
- 队列内部实现了锁机制,开发的时候,并发操作都是会用队列完成,队列是线程安全的,可以多个线程共享变量
- queue模块
- q=queue.Queue(maxsize):创建先进先出队列对象 maxsize指定队列最大的容量,默认无限容量
- q.qsize():返回队列中元素的个数
- q.empty():判断队列是否为空,空True
- q.full():判断队列是否已满,满True
- q.put(元素,block,timeout):向队列中加入元素
- block阻塞状态:默认值是true,当队列已满,继续put,会是程序阻塞
- timeout:指定等待阻塞的时间,必须block=Ture才有效
- q.get(block,timeout):从队列中取元素,get之后,元素就没有了
- bolck阻塞状态:默认True,当队列已空,继续get,会使程序阻塞
- tmoeout:指定阻塞的时间,必须bolck=True擦爱有效
- queue.LifoQueue():创建先进后出队列(堆栈)对象
- 方法同上但是get时是先进后出
- queue.PriorityQueue():优先队列
- 是根据元素的优先原则排列,出队列
- 方法同上
- 基本概念
- 进程
- 因为全局解释器锁的存在,在python里经常使用多进程来代替多线程,可以避免受GIL的限制
- 进程的创建
- multiprocessing模块
- (1)通过multiprocessing下Process类创建子进程
- (2)继承Process,重写run方法
- (3)进程池
- 进程一定要写在“主”程序中
- 进程的相关操作
- (1)os.getpid() 当前的进程id
- (2)os.getppid() 当前进程的父进程pd
- (3)os.fork() 复制进程(在windows下不支持)
- (4)p.run\start\is_alive\name\daemon
- 进程队列
- 两个进程之间的区域不共享,所以进程不存在进程不安全的问题,但是进程队列需要解决资源的通信问题。
- 解决方式:pickle序列化解决进程之间的资源通信(同步)
- 在向一个进程中修改队列的时候,同时向一个进程使用pickle序列化的方式同步队列的信息,在向另外一个进程中将序列化信息再回复成对象
- 网络编程
- 计算机网络
- 定义:将地理位置不同,具有独立功能的多台计算机,通过通信线路(不是通过硬件存储)来进行互相实现数据共享。
- 网络的七层结构
- 应用层(最贴近用户):http ftp
- 表示层
- 会话层
- 传输层: tcp udp
- 网络层: ip
- 数据链路层(设备和驱动)
- 物理层(最贴近计算机)
- 协议
- Http超文本传输协议
- ip互联网协议
- Tcp传输控制协议----安全有效的协议
- 基于连接的协议,点对点的通道,tcp协议可以保证一方发送之后,另一方一定能准确(接收顺序)的接收
- udp用户数据协议----无连接,不安全可靠协议
- 不管对方是否接收,发送方直接发送
- ftp文件传输协议
- url:统一资源定位,网络资源地址
- url分为两个部分(//分隔):协议的标识符 资源的名称
- python中解析url
- urllib.parse下urlparse函数,返回值是元组(协议,路径,参数...)
- 发送请求
- 两种方式
- (1)urllib.request的urlopen方法,返回值:response对象
- (2)urllib.request的urlretrieve方法:单纯的下载 参数1:访问的url 参数2:保存的文件
- 两种方式
- 套接字编程
- 客户端和服务端的通信
- 客户端向服务端发送请求
- 服务端监听的socket一直监听信息,可以接受到客户端传递过来的信息(新的socket)
- 套接字 socket
- 实例化一个socket对象 两个参数:地址组--AF_INET 通信类型--SOCK_STREAM tcp SOCK_DGRAM udp
- s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
- 服务端
- 绑定ip和端口号
- s.bind(("127.0.0.1",8888))
- 服务端server的socket进行监听
- s.listen()
- 接受客户端的请求--返回新的socket还有客户端的地址
- so,addr=s.accept()
- 接受服务端真正信息
- content=so.recv(1024)
- 服务端给客户端发送信息
- so.sendall(("2222").encode())
- 绑定ip和端口号
- 客户端
- 客户端client连接
- s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
- s.connet(("127.0.0.1",8888))
- 通过socket对象的sendall发送信息
- s.sendall(("111111").encode())
- 客户端接受服务端信息 recv是一个阻塞方法,如果没有sendall,就不会执行recv
- content=s.recv(1024).decode()
- 客户端client连接
- 实例化一个socket对象 两个参数:地址组--AF_INET 通信类型--SOCK_STREAM tcp SOCK_DGRAM udp
- 客户端和服务端的通信
- 协程
- 协程是比线程更小的执行单元,比起线程,线程切换更容易,更可控
- python 具有全局解释器锁的语言,需要使用协程解决更多的并发问题
- 高阶函数
- 至少满足:接受一个或者多函数为输入,或者输出一个函数
- 闭包就是高阶函数
- 经常使用的高阶函数
- (1)sort
- (2)map:把迭代对象中的元素使用函数的规则进行改变
- 语法:map(fuc,iterator)
- (3)reduce:按照函数 二变一
- 语法:reduce(func,iterator,初始值)
- (4)filter: 按照函数过滤
- 语法:filter(func,iterator)
- 对迭代对象中的每一个元素调用一次函数,获得返回值的结果true或者false,只获得返回值是true的元素
- (5)max:可以传入key函数的名字,来指定规则
- 默认按照第0个元素获取最大值
- 计算机网络
- 迭代器 生成器 装饰器
-
高阶
- 并发编程
- 1.进程、线程、协程、异步、同步、阻塞、非阻塞、并行、并发
- 基本概念
- 1.同步和异步
- 描述的是多个(线程、进程)相互之间的关系,关注的是消息通信机制,行为方式
- 同步:调用者主动等待被调用者返回结果,在没有返回结果之前就一直专职等待。
- 异步:调用者发送请求之后,不会专职等待被调用者返回结果,而是当被调用者有结果之后,通知调用者,然后调用者再回头继续做刚才的任务。
- 2.阻塞和非阻塞
- 关注的是程序在等待调用结果的时候的状态
- 阻塞:在调用结果返回之前,当前的线程会被挂起,调用的线程只有在得到结果之后才可以继续向下执行。
- 非阻塞:在调用返回结果之前,当前的线程不会被挂起,而是继续执行其他的动作。
- 同步异步和阻塞非阻塞的区别:
- 阻塞和非阻塞与同步和异步之间没有必然的联系,是两个概念,从两个角度解释的任务
- 阻塞是同步机制的结果。 非阻塞是异步机制的结果。
- 面试小难题:
- (1)非阻塞是不是一定意味着异步?
- 不是,非阻塞只能意味着,方法调用后,并不阻塞当前的任务,但是到底要不要继续调用其他的线程,那不是非阻塞决定的,是异步决定的。
- (2)阻塞是不是意味着任务之间一定不是异步的关系?
- 是的,阻塞意味着任务之间一定不是异步的关系。
- (3)异步不只意味着方法是非阻塞的,同时意味着工作会转移到其他线程。
- (4)同步一定意味着,在某一个时刻,某一个任务是处于阻塞状态。
- (1)非阻塞是不是一定意味着异步?
- 3.串行和并行
- 串行:多个任务按照顺序执行。从概念上可以理解成多个任务排队执行。
- 并行:多个任务同时执行。宏观上和微观上,分为并行和并发。
- 4.并行和并发
- 都是同时处理的含义
- 并行才是真正的同时处理
- 区分并行和并发----可以理解成 多核与单核
- 时间上:
- 并行:多个事件在同一时刻发生
- 并发:多个事件在同一时间间隔发生
- 作用点:
- 并行:作用在多个实体上的多个事件
- 并发:作用在同一个实体上的多个事件
- 处理个数:
- 并行:多个处理器处理多个任务
- 并发:一个处理器处理多个任务
- 时间上:
- 举例:
- 发试卷:
- 串行:1个人,给每个人挨个发试卷
- 并行:班长1 班长2 两个人同时发试卷
- 并发:对于班长1 既要发试卷又关门
- 发试卷:
- 5.并发的三个层次
- 1.低阶:对于底层的操作系统实现并发。高端
- 2.中阶:高级语言大多都支持中阶并发。编程语言上实现的并发。创建多线程,多进程
- 3.高阶:对中阶的实现进行了封装。第三方模块开发好的框架。
- 1.同步和异步
- 基本概念
- 2.多线程和多进程
- 多进程mutiprocessing.Process
- 一个程序在执行之后会创建一个或者多个进程
- 创建进程的方式
- 1.mutiprocessing.Process 指定target args,创建进程
- 2.继承mutiprocessing.Process 重写run方法
- 3.其他创建方式
- 其他创建方式 进程池
- 进程池:负责维护一定数量进程,可以向进程池提交任务,如果进程池中的进程数量没有达到上限,则会创建一个新的进程,执行任务,否则会在进程池外等待,等待进程池内进程死亡或者结束,才能再次创建新的进程,接受任务。
- 使用进程池创建进程(mutiprocessing.Pool 包)
- Pool(maxsize):maxsize进程池中进程数量上限
- (1)同步申请:进程池.apply 方法
- apply(func,args=())
- 创建完一个进程结束后,再创建另外一个进程。无论是子进程之间还是主进程和子进程之间都是串行
- (2)异步申请:进程池.apply_async方法
- apply_async(函数名,args=(函数参数),callback=函数名)
- 进程池实现的异步申请,子进程类似于后台进程,主进程执行完毕,不再执行子进程。
- join方法:能够让子进程抢占时间片,强迫阻塞主进程。
- 使用join之前必须使用close或者terminate,保证进程池中的进程数量,不再接受新的进程申请了。
- close:暂时关闭进程池,不接受新任务
- terminate:直接关闭进程池
- 回调函数(callback)
- 当希望多个任务完成之后执行相同函数
- 需要定义一个参数,参数是任务的返回值。
- 多线程threading.Thread
- 线程的创建
- (1)threading.Thread(target=,args=)
- (2)继承threading.Thread 重写run方法
- (3)使用线程池
- 线程池
- 第一种:mutiprocessing.pool 下的 ThreadPool
- 异步申请:
- apply_async()
- 同步申请:
- apply()
- 异步申请:
- 第二种:threadpool
- (1)引入threadpool模块
- (2)定义线程函数
- (3)创建线程池--threadpool.ThreadPool(maxsize)
- (4)创建需要线程池处理的任务列表tgreadpool.makeRequests(任务,参数列表)
- 参数列表::[(参数1,参数2),(字典型参数-没有写None)]
- (5)将创建的多个任务put到线程池中--线程池对象.putRequests(任务)
- (6)等到所有任务处理完毕--线程池对象.wait()---与先用colse再用join效果类似
- 安装第三方包:pip install 包名
- 第一种:mutiprocessing.pool 下的 ThreadPool
- 线程的创建
- concurrent.futures包下的多进程和多线程
- 抽象类(一个类不能直接创建对象,可以被继承)Executor:是一个上下文管理器
- ThreadPoolExecutor:线程池异步调用
with ThreadPoolExecutor(max_workers=4) as executor:# pass- max_workers 最大线程或者进程数,默认是4
- executor 相当于每一个进程或者线程的调用者,负责将线程或者进程加入到线程池或者进程池
- 操作方法:
- submit(函数名,*args,**kwargs): 返回值是即将要异步执行的任务
- fun:任务函数(需要被异步执行的函数)
- *args,**kwargs:函数的参数
- 结果:任务.result()任务返回值
- running():可以显示执行那一刻的线程运行状态
- done():执行哪一刻线程完成状态
- as_completed(待执行的任务列表,timeout):获得已经执行完毕的任务--需要导入才能使用
- 不能保证按照原来list任务列表顺序执行
- wait(待执行的任务列表,timeout,return_when):返回一个元组,一个是执行完的任务,一个是没有执行完的任务
- return_when:ALL_COMPLETED--程序会一直阻塞带线程池里所有任务全部完成
- FIRST_COMPLETED--保证至少一个完成
- map(函数名,函数参数):取代submit操作,返回值是任务调用的返回值集合,能够确保任务执行完毕,相当于使用as_completed
- zip转置--使用时还需* list(zip(* l))拆包
- map与submit区别
- map代码简洁,执行之后,顺序是按照原来任务的顺序。
- 但是不能够给map执行不同的任务。
- submit一般习惯跟as_completed,不按照任务的顺序执行。
- 但是可以执行不同的任务
- submit(函数名,*args,**kwargs): 返回值是即将要异步执行的任务
- 多进程mutiprocessing.Process
- 3.协程coroutine
- 基本概念
- 线程、进程都是系统级别的,是系统来统一调度分配时间片
- 协程是程序级别的,是开发者根据自己的需要调度
- 协程其实就是生成器实现的
- 协程可以利用yield来进行程序直接的切换或者调度
- 协程的优势
- 协程有很高的效率,只需要程序内部的yield来对程序进行自身控制,不需要线程的切换
- 协程没有线程安全问题
- 协程需要激活:next或者send(None)
- 协程的劣势:
- 协程+多进程可以应用多核cpu,无法自己使用多核cpu
- yield会阻塞整个程序,需要找好yield的位置
- 协程的实现
- 生成器的顶层是迭代器,当需要获取生成器的内部元素,可以使用next或者send或者for遍历
- next内置方法,只能获得生成器的元素,只能让生成器产出值
- send属于生成器对象的方法,即能让生成器产出值,同时还可以通过send(参数)给生成器传入值
- send的返回值是生成器的产出值
- send的参数是生成器表达式的值
- 协程的相关状态
- 显示方法:inspect包下的getgeneratorstate(协程对象)
- GEN_CREATED:协程创建好了,等待开始执行
- GEN_RUNNING:协程正在运行
- GEN_SUSPENDED:协程遇到yield
- GEN_CLOSED:协程执行结束
- 协程的关闭和异常
- 关闭
- 协程对象.close()
- 异常
- 生成器对象.throw():抛出异常
- 在发生异常的时候,yield的产出值会成为调用throw()方法的返回值
- 生成器对象.throw():抛出异常
- 关闭
- 协程的返回值
- 生成器的return返回值可以在生成器出现StopItertion异常的时候,调用者捕获异常时,会被异常对象value吸收
- yield from
- (1)替代生成器中的for循环
- yield from 范围
- (2)能够在调用生成器之后,再继续使用yield产出值
- yield from的生成器被称为委托生成器
- yield from还可以调用另外一个子生成器,获得子生成器抛出异常时候的return返回值
- 使用注意事项:
- (1)通过yield from获得子生成器的return值,客户端不许直接调用子生成器获得值
- (2)在调用委托生成器来出发子生成器,可以传入必要的参数
- (3)在最后一定要给委托生成器传一个None,使得委托生成器的yield from进行赋值,使得客户端得到结果
- (1)替代生成器中的for循环
- 命名元组nametuple
- from clooections import nametuple
- 给元组中的每一个元素都有一个名字
- 创建命名元组(三种方式)
- nametuple(“元组的名称”,[元素的名称1,元素的名称2......])
- greenlet包
- 实现手动切换
- switch()
- gevent包
- 可以实现自动切换
- 创建协程gevent.spawn(func)
- gevent.join(单个协程)
- gevent.join([所有协程])
- gevent.sleep:不是time.sleep,自己本身也是一个协程,可以对协程进行分配
- asyncio包
- 支持协程的异步
- 协程的定义
- 1.协程函数:指的是一个使用async关键字定义的函数
- 2.协程对象需要注册到【事件循环】,由事件循环调用---事件循环:程序在执行协程的时候,会开启一个无限循环,程序员会将一些函数注册到事件循环上,协程对象才可以执行
- 3.task任务/future:代表一个协程对象就是一个原生可挂起的函数
- 4.iscoroutinefunction():通过函数能够判断是不是一个协程函数
- 5.get_event_loop:【事件循环】
- 6.await 关键字:用来挂起阻塞异步调用
- 7.ensure_future:将协程加入到任务中
- 8.loop.run_util_complete:将任务以阻塞形式调用,知道协程运行结束,才继续执行主程序
- 9.add_done_callback:向任务future中加入回调函数
- 10.sleep:本身也是协程
- 协程可以做什么?
- 协程可以做两件事
- 第一:执行当前的future直到结束
- 第二:可以等待另外一个协程的调用
- 协程可以做两件事
- 协程的运行
- 有两种方式:通过ensure_future函数,计划执行协程对象,需要将协程对象加入到事件循环
- 通过await调用协程,被调用的协程可以执行
- 1需要定义事件循环
- 事件循环=asyncio.get_event_loop()
- 2将任务加入到执行计划future中,ensure_future可以返回一个任务列表
- async.ensure_future(函数(参数))
- 3需要指定执行的方式:(阻塞/不阻塞)一般指定run_util_complete的形式调用,协程任务将以阻塞的形式调用
- 事件循环.run_until_complete(任务列表)
- 回调函数
- 定义回调函数:一定是普通函数
- callback的参数是任务future,通过调用future.result可以获得协程的返回值
- 基本概念
- 1.进程、线程、协程、异步、同步、阻塞、非阻塞、并行、并发
- 并发编程
python基础
最新推荐文章于 2024-05-09 19:32:53 发布
本文详细介绍了Python编程的基础知识,包括变量、常量、数据类型、异常处理、文件操作、模块和包的使用。深入探讨了面向对象编程中的类、装饰器和多线程,并介绍了并发编程中的线程、进程和协程。此外,还涵盖了网络编程、数据结构和算法,以及常用的Python库和框架。
摘要由CSDN通过智能技术生成