Python
2019-07-31 21:10:43 229 举报
AI智能生成
python基础知识梳理
作者其他创作
大纲/内容
模块与内置函数
模块
### 模块的定义<br> 解决的问题:不易维护,效率低,<br>如:有10个文件,每个文件有50个函数,有一些相同功能或者相似功能的函数,代码冗余,重复性高,我们应该讲这10个函数提取出来,放在一个文件中,随用随拿.<br> 优点:节省代码,容易维护,组织结构更清晰
### 模块的分类:<br> 内置模块,标准库,Python解释器自带的,time os sys等等 200多种<br> 第三方库,各种大神写得一些模块,通过pip install...安装<br> 自定义模块,自己写得模块
#引用模块发生的三件事:<br>1.将.py文件加载到内存.<br>2.在内存中创建一个以.py文件命名的名称空间<br>3.通过.py文件的名字的名称空间的名字.等方式引用此模块的名字(变量,函数名,类名等等)<br>#坑:<br> 通过import引入,使用.的方式引用模块的名字时,一定是从此模块中寻找.<br> 通过import引用模块, 他有自己的独立名称空间,与当前执行文件没有关系
# 将一个比较长的模块名化简成简单的<br>import xxxxxx as aa<br>1. 书写方便<br>2. 简化代码<br>from x import xx as xxx
from... import...
#相当于从.py文件的全局变量中将变量与值得对应关系 复制到当前执行文件的全局名称空间中.<br>优点:使用起来方面<br>缺点:容易混淆<br> 坑:引用模块中的函数,如果此函数用到了一些变量,这些变量还说从此模块中寻找,不会改变当前执行文件的变量<br> #导图多个名字<br>from x import xx<br>from x import xx2<br>from x import xx3
py文件的两种功能
1,py文件的第一个功能:执行文件(承载代码)脚本.<br> 直接打印`__name__` 返回 `__main__`
2.py文件的第二个功能:模块(被执行文件)<br> 直接打印`__name__` 返回 模块名<br> 作用:用来控制.py文件在不同的应用场景下执行不同的逻辑(或是在模块文件中测试代码)
### 模块的搜索路径<br>寻找模块的路径:内存----> 内置模块----> sys.path中<br>只要这三个地方:内存,内置模块,sys.path这三个地方可以找到这个引用的模块的路径,这个模块就可以直接引用到.
import sys<br>sys.path.append(r" ")<br>#手动添加模块路径
内置模块
序列化模块json
序列化:将一种数据结构(list,dic....)转化成一个(特殊的字符串)序列的过程.<br>序列化模块:将一种数据结构转化成特殊的序列(特殊的字符串,bytes)并且还可以反转回去
#json模块 是所有语言公认的一种序列. 最最常用的<br> 所以Python的支持的数据结构有限: int float str bool dict list(tuple) None
#json序列化 两对 四个方法<br>#dumps,loads 主要用于网络传输.也可以用于文件的存取<br>import json<br>dic = {"username":"lyoko","passwd":123}<br>ret = json.dumps(dic)<br>print(ret.type(ret))<br>ret_dict = json.load(ret)<br>print(ret_dict,type(ret_dict))<br># 特殊的参数 <br>dic = {"username":"lyoko","passwd":123}<br>ret = json.dumps(dic,ensure_ascii=False, sort_key=True)<br>print(ret,type(ret))<br>#dump load 单个数据的存取文件<br>import json<br>dic = {"username":"lyoko","passwd":123}<br>with open("jsonlx1.json", encoding = "utf-8", mode = "w") as f1:<br> json.dump(dic.f1)<br> <br>with open(jsonlx1.json, encoding = "utf-8") as f1:<br> dic1 = json.load(f1)<br>print(dic1,type(dic1))
#pickle模块 只能是Python语言中使用的,序列化模块,支持Python所有的数据类型和对象
dumps,loads 只能是网络传输用
import pickle<br>l1 = ["阿里巴巴", "滕循", "小黑", 666]<br>ret = pickle.dumps(l1)<br>print(ret)<br>l2 = pick,load(ret)<br>print(l2, tupe(l2))
dump,load 数据结构存取文件
imprt pickle<br>l1 = ["阿里巴巴", "滕循", "小黑", 666]<br>with open("pickle练习.pickle" ,mode = "wb") as f1:<br> pickle.dump(l1,f1)<br> <br>with open("pickle练习.pickle", mode = "rb") as f1:<br> ret = pickle.load(f1)<br> print(ret, type(ret))
#多个数据写入文件
l1 = ["阿里巴巴", "滕循", "小黑", 666]<br>l2 = ["阿里巴巴", "滕循", "小黑", 777]<br>l3 = ["阿里巴巴", "滕循", "小黑", 888]<br>with open("pickle练习.pickle" ,mode = "wb") as f1:<br> pickle.dump(l1,f1)<br> pickle.dump(l2,f1)<br> pickle.dump(l3,f1)<br> <br>with open("pickle练习.pickle" ,mode = "rb") as f1:<br> ret1 = pickle.load(f1)<br> ret2 = pickle.load(f1)<br> ret3 = pickle.load(f1)<br> print(ret1,ret2,ret3)
os<br>
目录:当前文件夹(工作目录,当前目录,父级目录)
#和目录相关的<br>os.getcwd() #获取当前工作目录(绝对路径)<br>os.chdir() #改变当前工作目录(绝对路径)<br>os.chdir() #返回当前目录<br>os.pardir() #获取当前目录的父目录<br>#和文件夹相关的<br>os.makedirs() #创建文件夹 <br>os.removedirs() #删除文件夹 删除截止到有文件的文件夹<br>os.mkdir() #生成单级目录<br>os.rmdir() #删除单级目录<br>os.listdir() #打印路径至下的文件名至列表<br>#和文件相关的<br>os.remove() #删除<br>os.rename("旧","新") # 文件改名<br>os.stat("path/filename") # 获取文件信息<br># path 和路径相关<br>os.path.adspath() # 返回文件的绝对路径<br>os.path.split() # 将path分割成目录和文件的元祖返回<br>os.path.dirname()#返回path的目录就是os.path.split()前面内容(父级目录<br>print(__file__) # 动态获取当前的绝对路径<br>os.path.basename() #获取文件名<br>os.path.exist() #判断路径是否存在 返回布尔值<br>os.path.isabs() #判断是否是绝对路径<br>os.path.isfile() #判断路径是否是表示一个文件<br>os.path.isdir() #判断是否是一个目录 返回布尔值 <br>os.path.join("D:","S1","随便") # 拼接路径 ----> D:S1\随便<br>os.path.getatime(path) # 最后访问的时间<br>os.path.getmtime(path) # 最后修改的时间<br>os.path.getsize(path) # 文件的大小
sys
sys.path 获取模块的所有路径<br>sys.version 获取py版本<br>sys.exit(n) 主动退出 quit()
hashlib
加密模块,摘要算法 散列算法等等 他是一堆加密算法的集合.
hashlib如何加密?<br> 1.将一个bytes类型的数据 通过hashlib进行加密 返回一个等长的16进制数字<br> 2.过程不可逆.<br> 3.相同的bytes类型的数据,通过相同的加密方法得到的数据绝对相同<br> 4.不相同的bytes类型的数据,通过相同的加密方法得到的数据绝对不同
md5系列
import hashlib <br># md5<br>ret = hashlib.md5()<br>ret.update("123",encode(utf-8))<br>print(ret.hexdigest())
概念:撞库
加固定盐
import hashlib <br># md5<br>ret = hashlib.md5("哈哈哈".encode("utf-8"))<br>ret.update("123",encode(utf-8))<br>print(ret.hexdigest())
加动态盐
import hashlib <br># md5<br>ret = hashlib.md5("哈哈哈".encode("utf-8"))<br>ret.update("123",encode(utf-8))<br>print(ret.hexdigest())
校验文件的一致性
import hashlib<br>def md5_file(path):<br> ret = hashlib.md5()<br> with open(path, mode = "rb") as f1:<br> while 1:<br> content = f1.read(1024)<br> if content:<br> ret.update(content)<br> else:<br> return ret.hexdigest()
time
与时间相关的模块
三种形式 :<br> 1.时间戳 time.time() 时差,计时<br> 2.人类看得懂的时间 格式化时间. 2019-6-28 12:00<br>3.格式化时间<br>
print(time.strftime("%Y-%m-%d %H:%M:%S"))<br>#字符串类型<br>ret = time.strftime(f"%Y{}%m%d %H:%M:%S")<br>print(ret.rofmat('年','月','日'))
datetime
# datatime模块<br>import datetime<br>now_time = datetime.datetime.now() # 现在的时间<br># 只能调整的字段:weeks days hours minutes seconds<br>print(datetime.datetime.now() + datetime.timedelta(weeks=3)) # 三周后<br>print(datetime.datetime.now() + datetime.timedelta(days=3)) # 三天后<br>print(datetime.datetime.now() + datetime.timedelta(hours=-5)) # 5小时前<br>print(datetime.datetime.now() + datetime.timedelta(minutes=15)) # 15分钟后<br>print(datetime.datetime.now() + datetime.timedelta(seconds=70)) # 70秒后<br>current_time = datetime.datetime.now()<br># 可直接调整到指定的 年 月 日 时 分 秒 等<br>print(current_time.replace(year=1977)) # 直接调整到1977年<br>print(current_time.replace(month=1)) # 直接调整到1月份<br>print(current_time.replace(year=1989,month=4,day=25)) # 1989-04-25 18:49:05.898601<br># 将时间戳转化成时间<br>print(datetime.date.fromtimestamp(1232132131)) # 2009-01-17
random
import random<br># 随机小数<br>random.random() #大于0且小于1之间的小数<br>random.uniform(1,3) #大于1小于3的小数<br># 随机整数<br>random.randint(1,5) # 大于等于1且小于等于5之间的整数<br>random,randrange(1,10,2) # 大于等于1且小于10之间的奇数<br><br># 随机选择一个返回<br>random,chicee([1,2,3,4,5]) # 随机选择一个<br>random,chicee([1,2,3,4,5],3) # 随机选择三个组合 随机返回多个,返回的个数为函数的第二个参数<br># 打乱列表顺序<br>item = [1,3,4,5,6]<br>random,shuffle(tiem) # 打乱次序
规范化文件目录
# settings.py: 配置文件,就是放置一些项目中需要的静态参数,比如文件路径,数据库配置,软件的默认设置等等
# src.py:这个文件主要存放的就是核心逻辑功能,你看你需要进行选择的这些核心功能函数,都应该放在这个文件中。
# common.py:公共组件文件,这里面放置一些我们常用的公共组件函数,并不是我们核心逻辑的函数,而更像是服务于整个程序中的公用的插件,程序中需要即调用。但是有一些是不需要这个装饰器认证的,它既是何处需要何处调用即可。比如还有密码加密功能,序列化功能,日志功能等这些功能都可以放在这里
# starts.py 程序启动的开关文件
# 类似于register文件:这个文件文件名不固定,register只是我们项目中用到的注册表,但是这种文件就是存储数据的文件,类似于文本数据库,那么我们一些项目中的数据有的是从数据库中获取的,有些数据就是这种文本数据库中获取的,总之,你的项目中有时会遇到将一些数据存储在文件中,与程序交互的情况,所以我们要单独设置这样的文件。
# log文件:log文件顾名思义就是存储log日志的文件。日志我们一会就会讲到,日志主要是供开发人员使用。比如你项目中出现一些bug问题,比如开发人员对服务器做的一些操作都会记录到日志中,以便开发者浏览,查询。
# README<br># 它需要说明以下几个事项:<br> # 软件定位,软件的基本功能。<br> # 运行代码的方法: 安装环境、启动命令等。<br> # 简要的使用说明。<br> # 代码目录结构说明,更详细点可以说明软件的基本原理。<br> # 常见问题说明。
bin<br> start.py<br>config<br> settings.py<br>core<br> src.py<br>db<br> register.txt<br>lib<br> common.py<br>log<br> log2019xxxxxx<br> <br>README.TXT
面向对象
类,对象的定义
类:具有相同属性和方法的一类事物.
对象:类的具体表现.
类名
查询类中的所有内容(静态属性(变量),动态属性方法等):类名.__dict__
操作类中的某个公有静态属性(变量): 类名.字段名称
执行类中的方法: 一般只是通过类名调用类方法,和静态方法。
面向对象的三大特性:
继承
单继承
执行父类的静态变量:
如果子类没有同名变量则可以直接 . 寻找
如果子类有存在与父类同名的静态变量则通过super().area,或者直接父类.变量名的方法查找
在子类中既执行本类又执行父类的方法
父类名.方法名(self,父类此方法所需参数)
super().方法名(父类此方法所需参数)
只执行父类的方法:子类中不能定义同名的方法。
多继承
经典类: 深度优先,一条路走到黑.
新式类:C3算法,通过mro方法可以查出父类的继承顺序。
新式类的继承具体算法链接:
继承的优点:
1,提高代码的复用性。
2,提高代码的维护性。
3,让类与类产生关系,使得代码更具关联性,增加耦合性(双刃剑)。
继承的缺点:
类的耦合性增强了。
封装
给对象封装属性
给一个类封装静态变量及方法
私有成员:私有静态字段,私有方法,私有属性:__变量
私有成员:类只要加载到内存,他会将所有的私有成员变成: _类名__变量名
何处调用: 只能在类内部调用,不能在派生类或者类外部调用
多态
Python默认支持多态,即一个变量可以代指不同的类型,不用规定死。比如函数的形参 func(a): 对于形参a来说,他可以代指任意数据类型。
python崇尚鸭子类型:长得像鸭子,他就是鸭子。
为什么要有面向对象
1,面向对象是将相关的功能(函数)的集合,让代码更加合理的分类。
2,站在上帝的角度去考虑问题,他实际是构造了一个公共模板,通过这个公共模板实例化N多个对象,使代码具有多样性,规范性。
对象
1,对象的形成以及执行流程: 类名()自动执行object类中的__new__方法,产生并返回一个对象空间,之后执行__init__方法,给这个对象空间封装属性.直到执行完毕.
对象查询对象的所有属性:obj.__dict__
对象查询对象属性:obj1.属性名
对象查询类中的静态变量:obj1.变量名
对象可以调用类的实例方法:obj1.函数名(self)
类的结构
第一部分:静态字段(静态变量).
私有静态字段:__变量名
公有静态字段
第二部分:动态方法.
实例方法:func(self),通过对象调用
私有方法:__函数名,类内部调用
属性:@property:将方法伪装成属性,为了更合理.
特殊方法
__init__:初始化方法,此方法主要是给对象封装属性
__new__:构造方法产生并返回对象的方法,通过类名+()触发
__call__方法:通过实例化的对象()触发.
__item__系列:
__getitem__:obj['name'] 就会触发__getitem__方法,并将'name'传入此方法中
__setitem__
__delitem__
__len__方法:len(对象) 触发
__hash__方法: hash(对象) 触发
__str__方法:print(对象)或者str(对象)或者格式化输出'%s' % 对象 触发
__repr__方法:repr(对象)或者格式化输出'%s' % 对象 触发
__eq__方法:对象 == 对象 触发
上下文管理器相关,对一个对象进行with操作时触发这两个方法
__enter__方法
__exit__方法
代码示例
类方法
@classmethod(cls): 类名去调用,他会将类空间传给cls
类方法的作用:就是想要类名调用,去操控这个公共模板的内容(属性,方法等)
静态方法
@staticmethod():不用传入类空间,对象空间的方法
私有成员
类的成员按照公有私有可以分为两种:私有成员,公有成员。
私有成员分为三种
私有类的静态属性
私有对象属性
私有方法
代码示例
私有成员:只可以类中访问,不能再类外部以及派生类中访问。
空间,以及查找顺序
类名()会产生一个对象空间obj,此空间中有类对象指针,对象可以通过这个类对象指针找到从属的类。
对象查询属性:先从对象空间去找,对象空间没有,通过类对象指针去所属类去找
类查找顺序:先从本类去找,如果没有从父类去找,.......
类与类之间的关系
类与类之间原本没有关系,但是为了程序的拓展,增加代码之间的耦合性,可以让类产生关系。
依赖关系
依赖关系是类与类之间耦合性最低的关系,就是将一个类的类名或者对象传入到另一个类的方法中
关联,聚合,组合关系
给一个类的对象封装一个属性,这个属性是另个类的对象
实现,继承关系
实现也是继承关系,这个会在面向对象的三大特性中详细说明。
类的约束
方式一:提取⽗类. 然后在⽗类中定义好⽅法. 在这个⽅法中什么都不⽤⼲. 就抛⼀个异常就可以了. 这样所有的⼦类都必须重写这个⽅法. 否则. 访问的时候就会报错.
方式二:使⽤元类来描述⽗类. 在元类中给出⼀个抽象⽅法. 这样⼦类就不得不给出抽象⽅法的具体实现. 也可以起到约束的效果.
网络编程
网络初识
C/S B/S架构
C:client 客户端<br>B:Browers 浏览器<br>S:Service 服务端<br>C/S 客户端与服务器之间的架构 :QQ 微信 APP等 都属于C/S架构<br> 优点:安全性高,个性化设置,功能全面,相应速度快<br> 缺点:开发成本高,维护成本高,面向客户固定<br>B/S架构属于C/S架构, 浏览器-服务器之间的架构<br> 优点:开发维护成本低,面向用户广泛<br> 缺点: 安全性相对低,响应速度相对较慢,个性化设置单一
互联网通讯的原理
ISO七层协议(五层)
1.物理层:一系列的物理连接介质
发送的数据就是0101001101010001比特数据流,这些数据连续不断地收发.比特数据流要进行分组(按照一定规则) 数据分组这件事不是物理层的功能,要由数据链路做
2.数据链路层: 以太网协议
是按照一定的协议对比特流数据进行分组.<br>以太网协议:就是对数据进行分组;一组叫做一帧(数据报),<br> 每一数据帧分为:报头head和数据data部分<br> 数据头(head) | data数据 <br> 数据头:固定长度 18个字节 为了提取源地址以及目标地址<br> 源地址,目的地址,数据类型 666<br> data数据: 最短46字节<=data<=1500字节<br>网线直接接触的硬件就是网卡,网卡上有一个地址,Mac地址,确定计算机的唯一性的物理地址 网卡上:12位16进制组成的一串数字,前六位:厂商编号 后六位:流水线号
广播: 计算机最原始的通讯方式就是吼
数据的分组(源地址,目标地址)+广播 理论上我的计算机就可以通信了. 但效率太低,每台计算机都需要接受广播的消息,查看是否是给自己的数据<br>所以:广播他是有范围的,在同一子网,局域网内是通过广播的方式发小时
3.网络层: IP协议:确定对方的局域网的位置
广播,Mac地址+ip == 可以找到世界上任意一台计算机<br>计算机的通讯:计算机的软件与服务器的软件进行的通讯
4.传输层: 端口协议
TCP UDP协议
5.应用层:软件自己定义的协议.<br>
QQ:发送数据'今晚请我吃饭...' ---> {id:'username', content:'今晚请我吃饭...'}<br>将数据按照自己定义的协议进行封装
# 数据经过以太网协议封装吼,先要从局域网内进行广播,每次发消息,每次都要广播,这样效率是很低的.<br>广播:计算机的吼 <br>单播:单线直接联系<br># RP协议:通过将对方的ip地址获取到对方的Mac地址<br># ip协议:ip地址加子网掩码确定计算机所在的网段,子网,局域网的位置<br>ipV4地址:四点分十进制<br> 192.168.1.1 取值范围 0~255<br>子网掩码 中国都是C类网 最多可以分配的IP数量为254个<br>255.255.255.0<br>一个局域网最多可以放置254个IP地址(同一个局域网的IP地址唯一)<br><br># 物理层---->数据链路层(以太网协议(Mac地址))---->网络层(ip协议)---->传输层(端口协议,TCP/udp)----> 应用层<br>Mac地址+广播形式+IP地址+端口 == 锁定全世界范围的任意一个计算机的某软件的位置<br>ip地址 + 端口 == 锁定全世界范围的任意一个计算机的某软件的位置<br>传输层:端口协议<br> TCP协议,UDP协议<br>端口: 0 ~ 65535端口号<br> 1~1023 系统占用的端口号<br> 1024~8000软件占用的端口号
UDP和TCP
TCP的三次握手四次挥手
# 三次握手<br>客户端 与 服务端第一次建立通信联系 需要三次'握手'<br> 客户端向服务端发送syn=1 seq=序列号至服务端<br> 服务端返回 ack=1+x syn=1 seq=y <br> 客户端向服务端发送ack=1+y<br> ack:确认信号 syn:请求建立连接的信号 seq:发送的数据信号<br># TCP协议:<br> 优点:好人协议,不会拒绝别人,稳定,安全<br> 缺点:效率低<br>syn洪水攻击:黑客会虚拟很多的假ip,然后访问你的服务器. 半连接池,缓冲效果.<br> <br># udp协议:<br> 优点:效率高,传输快<br> 缺点:不安全,不是面向连接的,不是很可靠
# 四次挥手<br>客户端 与 服务端结束通讯联系 需要四次'挥手'<br> 客户端向服务端发送 fin=1 seq=x<br> 服务端返回客户端 ack=x+1<br> 服务端向客户端发送 fin=1 seq=y<br> 客户端返回服务端 ack=y+1
socket
套接字:他存在于传输层与应用层之间的抽象层
单个客户端通讯
# client端<br>import socket<br># 1. 创建socket对象<br>phone = socket.socket() # 可以默认不写<br># 连接服务器ip地址与端口<br>phone.connect(('127.0.0.1', 8848))<br># 发消息<br>to_server = input('>>>').strip()<br>phone.send(to_server.encode('utf-8'))<br># 接收消息<br>from_server_data = phone.recv(1024) # 夯住,等待服务端的数据传过来<br>print(f'来自服务端消息:{from_server_data.decode("utf-8")}')<br># 关机<br>phone.close()
# server端<br>import socket<br># phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)<br># 1. 创建socket对象<br>phone = socket.socket() # 可以默认不写<br># 2. 绑定ip地址和端口<br>phone.bind(('127.0.0.1', 8848)) # 本地回环地址<br># 3. 监听<br>phone.listen(5)<br># 4. 接收连接<br># print('start')<br>conn, addr = phone.accept() # 程序夯住<br># print(conn,addr)<br>from_client_data = conn.recv(1024) # 至多接收1024个字节<br>print(f'来自客户端{addr}消息{from_client_data.decode("utf-8")}')<br>to_client = input('>>>')<br>conn.send(to_client.encode('utf-8'))<br>conn.close()<br>phone.close()
通讯循环
# client<br>import socket<br># 1. 创建socket对象<br>phone = socket.socket() # 可以默认不写<br># 连接服务器ip地址与端口<br>phone.connect(('127.0.0.1', 8888))<br># 发消息<br>while 1:<br> to_server = input('>>>').strip()<br> if to_server.upper() == 'Q':<br> phone.send('q'.encode('utf-8'))<br> break <br> phone.send(to_server.encode('utf-8'))<br> # 接收消息<br> from_server_data = phone.recv(1024) # 夯住,等待服务端的数据传过来<br> print(f'来自服务端消息:{from_server_data.decode("utf-8")}')<br># 关机<br>phone.close()
# server<br>import socket<br>phone = socket.socket()<br>phone.bind(('127.0.0.1', 8888))<br>phone.listen(5) # 最大连接数<br># 4. 接收连接<br>while 1:<br> print('start')<br> conn, addr = phone.accept() # 程序夯住<br> print(conn, addr)<br> while 1:<br> try:<br> from_client_data = conn.recv(1024) # 至多接收1024个字节<br> if from_client_data == b'q':<br> break<br> print(f'来自客户端{addr}消息{from_client_data.decode("utf-8")}')<br> to_client = input('>>>')<br> conn.send(to_client.encode('utf-8'))<br> except ConnectionResetError:<br> break<br> conn.close()<br>phone.close()
网络协议梳理
# 应用层: 软件自定制协议.FTP,HTTP.<br> 你形成了一个数据: '今天聚餐,别迟到'<br> 数据size, md5这两个参数组成了报头head<br> 飞信: data = {'size': 22, 'md5': 'e4rrewr345345643' ,'content': '今天聚餐,别迟到'}<br># 传输层: 端口协议. 封装了端口.确定软件在计算机的位置.<br> tcp(端口号: 9000) |data = {'size': 22, 'md5': 'e4rrewr345345643' ,'content': '今天聚餐,别迟到'}<br># 网络层: ip协议.<br> IP协议(源ip地址, 目标ip地址)|tcp(端口号: 9000) |data = {'size': 22, 'md5': 'e4rrewr345345643' ,'content': '今天聚餐,别迟到'}<br># 数据链路层: 以太网协议. mac地址<br> 以太网协议(源mac地址,目标mac地址 数据类型)|IP协议(源ip地址, 目标ip地址)|tcp(端口号: 9000) |data = {'size': 22, 'md5': 'e4rrewr345345643' ,'content': '今天聚餐,别迟到'}<br># 物理层: 转化成byte流发出去.
粘包
系统缓冲区
如果你的网络出现短暂的异常或者波动,接受数据就会出现短暂的中断,影响你的下载或者上传的效率.<br>但是 缓冲区也是双刃剑,缓冲区解决了上传下载的传输效率的问题,但是带来了粘包的问题
什么情况下产生粘包
1.recv会产生粘包(如果revc的接受数据量小于发送的数据量,第一次只能接受规定的数据量,第二次接受剩余的数据量)<br>2.send也可能造成粘包 (连续send少量的数据发到输出缓冲区,由于缓冲区的机制,也可能在缓冲区中不断积压,多次写入的数据被一次性发送到网络)
解决粘包的方案(revc的工作原理)<br>
# 错误实例<br>1.扩大recv:<br> 可以扩大recv的上线 revc(10240000000000000000) <br> 不是解决这个问题的根本原因 可能会撑爆内存<br>2.故意延长recv的时间 <br> time.sleep 这样会非常影响效率<br># 如何解决<br>解决粘包现象的思路分析:<br> 1.当我第二次给服务器发送命令之前,我应该循环recv直至将所有的数据全部取完.<br> recv的次数与循环次数相关 3000>>3次 5000>>5次 30000>>30次<br> 如何限制循环次数?<br> 当你发送的总bytes个数与接收的总bytes个数相等时,循环结束.<br> 如何获取发送的总bytes个数: len()<br> 所以: send两次 总个数 + 总数据<br> 总个数是什么类型? int() 3400,send需要发送 bytes类型.<br> send(总个数)<br> 将int 转化成bytes 即可. b'3400'<br> 方案一:<br> str(3400) -- > '3400' -----> bytes('3400') -----> b'3400' ---> 几个字节? 4个字节<br> send(总数据)<br>1563337531069<br>你现在继续解决的问题!!!!!<br>无论总字节个数是多多少? 129 3400 10000 30000 转化成固定长度的bytes.<br>将不固定长度的int类型,转化成固定长度bytes类型.方便获取头部信息.
解决沾包
server
import socket<br>import subprocess<br>import struct<br>import json<br>phone = socket.socket()<br>phone.bind(('127.0.0.1', 8888))<br>phone.listen(5)<br># 4.接收连接<br>print('start')<br>conn, addr = phone.accept()<br>while 1:<br> try:<br> cmd = conn.recv(1024)<br> obj = subprocess.Popen(cmd.decode('utf-8'),<br> shell=True,<br> stdout=subprocess.PIPE,<br> stderr=subprocess.PIPE,<br> )<br> result = obj.stdout.read() + obj.stderr.read()<br> result = result.decode('gbk').encode('utf-8')<br> # 1.制作表头<br> head_dict = {<br> 'MD5':'896a74sf9647a12we',<br> 'file_name':'xxx视频',<br> 'file_size':len(result),<br> }<br> # 2.将报头字典转化成json字符串<br> head_dict_json = json.dumps(head_dict)<br> # 3.将json字符串 转化成bytes<br> head_dict_json_bytes = head_dict_json.encode('utf-8')<br> # 4.获取报头的长度<br> head_len = len(head_dict_json_bytes)<br> # 5.将长度转化成固定的4个字节<br> head_len_bytes = struct.pack('i', head_len)<br> # 6.发送固定的4个字节<br> conn.send(head_len_bytes)<br> # 7.发送报头<br> conn.send(head_dict_json_bytes)<br> # 8.发送原数据<br> conn.send(result)<br> except ConnectionResetError:<br> break<br>conn.close()<br>phone.close()
client
import socket<br>import struct<br>phone = socket.socket()<br>phone.connect(('127.0.0.1', 8888))<br># 发消息<br>while 1:<br> cmd = input('>>>').strip()<br> phone.send(cmd.encode('utf-8'))<br> # 1.接收报头<br> head_bytes = phone.recv(4)<br> # 2.将报头反解回int类型<br> total_size = struct.unpack('i', head_bytes)[0]<br> # 3.循环接收原数据<br> total_data = b''<br> while len(total_data) < total_size:<br> total_data += phone.recv(1024)<br> print(total_data.decode('gbk'))<br>phone.close()
udp
pdu协议:不可靠,相对来说不安全的协议,面向数据报(无连接)的协议,效率高,速度快.
# client<br>import socket<br><br>udp_client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # 基于网络,udp协议的socket<br>while 1:<br> to_server_data = input('>>>').strip()<br> udp_client.sendto(to_server_data.encode('utf-8'),('127.0.0.1', 9000))<br> from_server_data = udp_client.recvfrom(1024)<br> print(f'来自{from_server_data[1]}的消息:{from_server_data[0].decode("utf-8")}')
# server<br>import socket<br><br>udp_server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # 基于网络,udp协议的socket<br><br>udp_server.bind(('127.0.0.1', 9000))<br><br>while 1:<br> from_client_data = udp_server.recvfrom(1024)<br> print(f'来自{from_client_data[1]}的消息:{from_client_data[0].decode("utf-8")}')<br> to_client_data = input('>>>').strip()<br> udp_server.sendto(to_client_data.encode('utf-8'),from_client_data[1])<br><br># udp不会产生粘包的现象 并且默认支持一对多(串行)
网络并发
# server
import socketserver<br><br>class MyServer(socketserver.BaseRequestHandler): # 继承的类固定的<br><br> def handle(self): # 必须是这个handle名字.<br> while 1:<br> from_client_data = self.request.recv(1024).decode('utf-8') # self.request == conn管道<br> print(from_client_data)<br> to_client_data = input('>>>').strip()<br> self.request.send(to_client_data.encode('utf-8'))<br><br>if __name__ == '__main__':<br><br> ip_port = ('127.0.0.1',8848)<br> server = socketserver.ThreadingTCPServer(ip_port,MyServer)<br> # server.allow_reuse_address = True<br> # print(socketserver.ThreadingTCPServer.mro())<br> # [ThreadingTCPServer, ThreadingMixIn,TCPServer, BaseServer]<br> server.serve_forever()
# client
import socket<br><br># 1. 创建socket对象(买手机)<br>phone = socket.socket() # 可以默认不写<br><br># 连接服务器ip地址与端口<br>phone.connect(('127.0.0.1', 8848))<br><br># 发消息<br>while 1:<br> content = input('>>>').strip()<br><br> phone.send(f'MC阿强:{content}'.encode('utf-8'))<br> # 接收消息<br> from_server_data = phone.recv(1024) # 夯住,等待服务端的数据传过来<br> print(f'来自服务端消息:{from_server_data.decode("utf-8")}')<br><br># 关机<br>phone.close()
并发编程
计算系操作系统基础知识
1. 进程基础知识<br><br> 1. 程序是什么?<br><br> 程序就是一堆文件.<br><br> 2. 进程是什么?<br><br> 进程就是一个正在执行的文件/程序,抽象的概念.<br><br> 3. 进程被谁执行?<br><br> cpu最终运行你的程序.,<br><br> 操作系统调度作用.将你的磁盘上的程序加载到内存,然后交由CPU去处理.一个cpu正在运行的一个程序,就叫开启了一个进程.<br><br>2. 操作系统<br><br> 1. 操作系统的定义<br><br> 操作系统是存在于硬件与软件之间,管理,协调,控制软件与硬件的交互.<br><br> 2. **操作系统的作用:**<br><br> 1. 如果没有操作系统,你去写一个程序,你只要完成两层即可.<br><br> 第一层: 你要学会底层硬件:cpu,内存,磁盘是如何工作使用的.<br><br> 第二层: 去调用这些底层的硬件.<br><br> **操作系统第一个作用:将一些丑陋复杂的硬件操作封装成美丽的接口,便于使用.**<br><br> open()<br><br> 1. 操作系统的第二个作用:<br><br> 你的计算机(单个cpu情况)看似可以处理多个进程,看电影 + 玩纸牌游戏 + qq聊天 + 12306抢票.....<br><br> **操作系统第二个作用: 合理的调度分配多个进程与cpu的关系,让其有序化**,<br><br>3. 操作系统(计算机)的发展史(多道技术)<br><br> 计算机: 机械式计算机.算盘.<br><br> 电子计算机:发展史.<br><br> 1. 第一代电子计算机(1940~1955)<br><br> 二战时期,推出的电子计算机 手工操作.预定.类似于插线板的程序,计入机房,一个人独资享有计算机2个小时,各种硬件的操作插线与你的程序结合.<br><br> 特点: 没有操作系统的概念,所有的硬件连接都是自己完成.<br><br> 优点: 一个人独享.<br><br> 缺点:<br><br> 1. 一个人享用浪费资源.<br> 2. 所有的程序都是串行处理.<br><br> 2. 第二代计算机:磁带存储,批处理系统.(1955~1965)<br><br> 不需要程序员本人继续硬件的操作,所有的硬件的操作都已经是成型的机器了.<br><br> 每个程序员将自己的程序可在磁盘上.就可以走了.中间有工作人员将你的代码磁盘运行,最后打印结果.<br><br> 优点: 节省了程序员连接个硬件的操作的时间.<br><br> 缺点:<br><br> 1. 不能独享计算机,不能在程序运行时修复bug.<br> 2. cpu还是串行处理.<br><br> 3. 第三代计算机,集成电路,多道程序系统.<br><br> 集成电路概念,所有的硬件变的很小,排列在线路板上.<br><br> 20世纪60年代初: 计算机两条生产线:互不兼容<br><br> 1. 用于科学计算,运算的 计算密集型.<br><br> 2. 用户商用保险银行,归档,打印等工作. IO 密集型.<br><br> **阻塞;** IO阻塞,recv, accept, read input,write, sleep等等,都是阻塞.<br><br> system/360系列: 将两种计算机类型合并成一个.<br><br> 第三代计算机解决一个问题: 人工中途参与磁盘传递工作,以及输入输出设备不同机的问题.<br><br> 第三代计算机解决第二个问题: 多道技术.<br><br> 知识点解释:<br><br> 办一个效率高,效率低的问题.<br><br> 你洗衣服,20分钟,20分钟,你(cpu)一直没有闲着.cpu处理的效率高(利用率,满负荷),<br><br> 洗衣服: 10分钟, 40分钟, # 以耗时最长的进程结束为准<br><br> 聊天 : 20分钟, 10分钟<br><br> 烧水: 3分钟, 5分钟<br><br> **如果三个进程全部都没有IO阻塞,多道技术就会影响最终的效率.**<br><br> 多道技术解决的第一个问题: **时间上的复用.**<br><br> 第一代,二代计算机一个内存只允许加载一个进程.<br><br> 第三代计算机开始,多道技术结局了提高内存的利用率: **空间上的复用.**<br><br> **一个内存可以加载多个进程**.<br><br> 空间上的复用: 当年面临着一个问题,数据隔离.<br><br> 麻省理工(MIT)在一台改装过的7094机上开发成功的,CTSS兼容分时系统.<br><br> 一个问题: 还是不能独享<br><br> 最终第三代计算机推广使用.<br><br>4. 进程介绍(理论部分)<br><br> 1. **介绍几个概念.**<br><br> 串行: 所有的进程由cpu一个一个的解决.<br><br> 并发:单个cpu,同时执行多个进程(来回切换的),看起来像是同时运行.<br><br> 并行:多个cpu,真正的同时运行多个进程.<br><br> 阻塞:遇到IO才叫阻塞.<br><br> 一个cpu运行两个进程,其中一个进程完全没有阻塞,<br><br> 非阻塞: 没有IO.<br><br> 2. 进程的创建.<br><br> 什么是开启多个进程: <br><br> python中,如果一次想开启多个进程,必须是一个主进程,开启多个子进程.<br><br> linux, windows: 由主进程开启子进程:<br><br> **相同点:** 原则:主进程开启子进程两个进程都有相互隔离的独立的空间,互不影响.<br><br> **不同点**:<br><br> linux: 子进程空间的初始数据完全是从主(父)进程copy一份.<br><br> windows: 子进程空间的初始数据完全是从主(父)进程copy一份,但是有所不同.
进程
多进程
from multiprocessing import Process<br>import time<br><br>def task(name):<br><br> print(f'{name} is running')<br> time.sleep(3)<br> print(f'{name} is done')<br>if __name__ == '__main__': # windows环境下,开启多进程一定放在这个下面<br> p = Process(target=task,args=('怼怼',)) # args 一定是一个元组的形式.<br> p.start()<br> # 通知操作系统,你给我在内存中开辟一个空间,将p这个进程放进去,然后让cpu执行.<br> print('===主进程')
方法2(类)
from multiprocessing import Process<br>import time<br><br>class MyProcess(Process):<br> def __init__(self, name):<br> super().__init__() # 必须要继承父类的__init__<br> self.name = name<br> def run(self): # 必须定义run名字.<br> print(f'{self.name} is running')<br> time.sleep(3)<br> print(f'{self.name} is done')<br><br>if __name__ == '__main__': # windows环境下,开启多进程一定放在这个下面<br> p = MyProcess('怼怼')<br> p.start()<br> # 通知操作系统,你给我在内存中开辟一个空间,将p这个进程放进去,然后让cpu执行.<br> print('===主进程')
获取进程以及父进程的pid
进程在内存中开启多个,每个进程都有一个唯一标识.
1.在终端查看进程的pid<br>cmd---->tasklist<br>2.在终端查看指定的pid<br>cmd----> tasklist | findstr pycharm<br>3.通过代码查看pid<br>import os<br>print(os.getpid())
.join的用法
from multiprocessing import Process<br>import time<br>def task(name,sec):<br> time.sleep(sec)<br> print(f'{name} is running')<br><br>if __name__ == '__main__':<br> p1 = Process(target=task, args=('李业', 1))<br> p2 = Process(target=task, args=('怼哥', 1))<br> p3 = Process(target=task, args=('mc骚Q', 1))<br> start_time = time.time()<br> p1.start()<br> p2.start()<br> p3.start()<br> # p1, p2, p3 三个子进程运行的先后顺序不定.<br> # start只是通知一下操作系统,三个start几乎同一时刻发给操作系统,<br> # 操作系统调用cpu先运行谁,谁先执行.<br> p1.join()<br> print(f'===主进程:{time.time() - start_time}之后,执行')
进程对象的其他属性
from multiprocessing import Process<br>import time<br>def task(name):<br> print(f'{name} is running')<br> time.sleep(3)<br> print(f'{name} is done')<br>if __name__ == '__main__':<br> p = Process(target=task,args=('怼哥',) ,name='任务1') # name给进程对象设置name属性<br> p.start()<br> # print(p.pid) # 获取进程pid号<br> # print(p.name)<br> # time.sleep(1)<br> p.terminate() # 终止(结束)子进程<br> # terminate 与 start一样的工作原理: 都是通知操作系统终止或者开启一个子进程,内存中终止或者开启(耗费时间)<br> # time.sleep(1)<br> # print(p.is_alive()) # 判断子进程是否存活<br> # 只是查看内存中p子进程是否运行.<br> print('===主进程')
守护进程与孤儿进程
# 主进程是子进程的发起者,按理说,主进程不会管子进程是否结束,对于结束来说,两个进程是没有任何关系的.<br># 但是通过代码我们发现: 主进程并没有结束,实际上你的主进程要等到所有的子进程结束之后,主进程在结束.<br><br># 所以此时的主进程称之为:僵尸进程: 僵尸是什么? 死而不腐.<br># 此时主进程形成了僵尸进程:<br># 内存中只包含: 主进程的pid,以及子进程的开启时间,结束时间. 至于主进程的代码以及文件,数据库数据等等全部消失.<br># 因为主进程要进行收尸环节.<br># 利用这个waitepid()方法,对所有的结束的子进程进行收尸.<br><br># 孤儿进程: 此时如果主进程由于各种原因,提前消失了,它下面的所有的子进程都成为孤儿进程了.<br><br>#一段时间之后, init就会对孤儿进程进行回收.<br><br># 孤儿进程无害,如果僵尸进程挂了,init会对孤儿进程进行回收.<br># 父进程(僵尸进程)无限的开启子进程,递归的开启,子进程越来越多,僵尸进程还没有结束,<br># 导致进程会越来越多,占用内存.
# 守护: 我守护者你,你要是死了,我就跟你一起.<br># 子进程对父进程可以进行守护.
# 将X子进程设置成守护进程,守护主进程,只要主进程结束,子进程无论执行与否,都马上结束.
进程间的通讯
互斥锁
from multiprocessing import Process<br>from multiprocessing import Lock<br>import time<br>import random<br><br><br>def task1(lock):<br> print('task1') # 验证cpu遇到io切换了<br> lock.acquire()<br> print('task1: 开始打印')<br> time.sleep(random.randint(1, 3))<br> print('task1: 打印完成')<br> lock.release()<br><br>def task2(lock):<br> print('task2') # 验证cpu遇到io切换了<br> lock.acquire()<br> print('task2: 开始打印')<br> time.sleep(random.randint(1, 3))<br> print('task2: 打印完成')<br> lock.release()<br><br><br>def task3(lock):<br> print('task3') # 验证cpu遇到io切换了<br> lock.acquire()<br> print('task3: 开始打印')<br> time.sleep(random.randint(1, 3))<br> print('task3: 打印完成')<br> lock.release()<br><br><br>if __name__ == '__main__':<br><br> lock = Lock()<br><br> p1 = Process(target=task1, args=(lock,))<br> p2 = Process(target=task2, args=(lock,))<br> p3 = Process(target=task3, args=(lock,))<br><br> p1.start()<br> p2.start()<br> p3.start()<br><br># 上锁:<br># 一定要是同一把锁: 只能按照这个规律:上锁一次,解锁一次.<br><br># 互斥锁与join区别共同点? (面试题)<br><br># 共同点: 都是完成了进程之间的串行.<br># 区别: join认为控制的进程串行,互斥锁是随机的抢占资源.保证了公平性
队列
Queue([maxsize]):创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递。<br>maxsize是队列中允许最大项数,省略则无大小限制.<br><br>1 q.put方法用以插入数据到队列中,put方法还有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,该方法会阻塞timeout指定的时间,直到该队列有剩余的空间。如果超时,会抛出Queue.Full异常。如果blocked为False,但该Queue已满,会立即抛出Queue.Full异常。<br>2 q.get方法可以从队列读取并且删除一个元素。同样,get方法有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,那么在等待时间内没有取到任何元素,会抛出Queue.Empty异常。如果blocked为False,有两种情况存在,如果Queue有一个值可用,则立即返回该值,否则,如果队列为空,则立即抛出Queue.Empty异常.<br>3 <br>4 q.get_nowait():同q.get(False)<br>5 q.put_nowait():同q.put(False)<br>6 <br>7 q.empty():调用此方法时q为空则返回True,该结果不可靠,比如在返回True的过程中,如果队列中又加入了项目。<br>8 q.full():调用此方法时q已满则返回True,该结果不可靠,比如在返回True的过程中,如果队列中的项目被取走。<br>9 q.qsize():返回队列中目前项目的正确数量,结果也不可靠,理由同q.empty()和q.full()一样
在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。<br>生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
线程
什么是线程
1. 线程就是一条流水线.<br><br> 什么是进程? 进程开启经历了什么?<br><br> 开启进程: 内存中开空间,加载资源与数据,调用cpu执行,可能还会使用这个空间的资源.<br><br> 进程: 主要任务:,开启空间,加载数据.<br><br> 线程: 流水线,执行代码.<br><br>进程: 划分空间,加载资源. 静态的.<br><br>线程: 执行代码.执行能力,动态的.<br><br>抽象的概念.<br><br>开启qq: 开启一个进程: 在内存中,开空间,加载数据. 启动一个线程执行代码.<br><br>线程是依赖于进程, 一个进程可以包含多个线程.但是一定有一个主线程. 线程才是cpu执行的最小单元.<br><br>1. 线程vs进程(理论)<br><br> 1. 开启多进程开销非常大. 10~100.开启线程开销非常小.<br> 2. 开启多进程的速度慢,开启多线程速度快.<br> 3. 进程之间数据不能直接共享通过队列可以.同一个进程下的线程之间的数据可以共享.<br><br>2. 多线程的应用场景介绍<br><br> 并发: 一个cpu来回切换(线程之间的切换). 多进程并发, 多线程的并发.<br><br> 多进程并发: 开启多个进程, 每个进程里面的主线程执行任务.<br><br> 多线程并发: 开启1个进程, 此进程里面多个线程执行任务.<br><br> 什么时候用多进程,什么时候用多线程?<br><br> 一个程序: 三个不同的任务.<br><br> 如果以后工作中遇到并发:多线程居多.
开启线程的两种方式
from threading import Thread<br>def task(name):<br> print(f'{name} is running')<br>if __name__ == '__main__':<br> t = Thread(target=task,args=('mcsaoQ',))<br> t.start()<br> print('主线程')<br><br> <br>from threading import Thread<br>class MyThread(Thread):<br> def run(self):<br> print(f'{self.name} is running')<br>if __name__ == '__main__':<br> t = MyThread()<br> t.start()<br> print('主线程')
线程与进程之间的对比<br>1. 速度的对比<br> 线程绝对要比进程要快:<br>2. pid<br>3. 线程之间共享资源
线程的其他方法
from threading import Thread<br>import threading<br>import time<br>def task(name):<br> time.sleep(1)<br> print(f'{name} is running')<br> print(threading.current_thread().name)<br>if __name__ == '__main__':<br> for i in range(5):<br> t = Thread(target=task,args=('mcsaoQ',))<br> t.start()<br> # 线程对象的方法:<br> # time.sleep(1)<br> # print(t.is_alive()) # 判断子线程是否存活 ***<br> # print(t.getName()) # 获取线程名<br> # t.setName('线程111')<br> # print(t.getName()) # 获取线程名<br> # threading模块的方法:<br> # print(threading.current_thread().name) # MainThread<br> # print(threading.enumerate()) # 返回一个列表 放置的是所有的线程对象<br> print(threading.active_count()) # 获取活跃的线程的数量(包括主线程)<br> print('主线程')
守护线程
# 守护: 子守护主, 只要主结束,子马上结束.<br># 多线程是同一个空间,同一个进程,进程代表 空间,资源. 静态的.<br># 主线程是进程空间存活在内存中的必要条件.<br># 主线程: 必须要等待所有的子线程全部结束之后,你在执行完毕,进程在消失.<br># 守护线程必须等待主线程结束才结束, 主线程必须等待所有的非守护线程结束才能结束.<br># 守护线程: 必须等待所有的非守护线程以及主线程结束之后才能够结束.
互斥锁(锁)
互斥锁与join区别?<br># 互斥锁 随机抢锁,公平. join 提前排好顺序,不公平.但是都是串行.<br>互斥锁,锁,同步锁都是一把锁.
死锁现象,递归锁
# 递归锁<br># 递归锁是一把锁,锁上有记录,只要acquire一次,锁上就计数1次, acquire2次,锁上就计数2次,<br># release1次,减一,<br># 只要递归锁计数不为0,其他线程不能抢.
信号量
# 锁都是只允许一个线程或者进程进入.<br># 信号量允许多个线程或者进程同时进入<br>from threading import Thread<br>from threading import current_thread<br>from threading import Semaphore<br>import time<br>import random<br>sm = Semaphore(4)<br># lock= Lock()<br>def go_public_wc():<br> sm.acquire()<br> print(f'{current_thread().name}正在厕所')<br> time.sleep(random.randint(1, 3))<br> sm.release()<br><br><br>if __name__ == '__main__':<br> for i in range(20):<br> t = Thread(target=go_public_wc)<br> t.start()<br><br> # print('主')
GIL锁.
介绍
GIL锁: 全局解释器锁. 就是一个把互斥锁,将并发变成串行,同一时刻只能有一个线程使用共享资源,牺牲效率,保证数据安全.<br><br>带来的问题1:<br> 单进程的多线程不能利用多核. 诟病之一.<br> 多进程的多线程可以利用多核.<br>带来的问题2:<br> 感觉上不能并发的执行问题.<br>讨论: 单核处理IO阻塞的多线程,与多核处理IO阻塞的多线程效率差不多.<br><br># 多核的前提下: 如果任务Io密集型: 多线程并发.如果任务计算密集型: 多进程并发.
GIL锁与互斥锁的关系.
GIL锁与互斥锁的关系.
1.GIL 自动上锁解锁, 文件中的互斥锁Lock 手动上锁解锁.<br>2. GIL锁 保护解释器的数据安全. 文件的互斥锁Lock 保护的文件数据的安全.<br> 线程全部是计算密集型:当程序执行,开启100个线程时,第一个线程先要拿到GIL锁,然后拿到lock锁,释放lock锁,最后释放GIL锁.
进程池线程池.
进程池: 放置进程的一个容器.<br>线程池: 放置线程的一个容器.
from concurrent.futures import ProcessPoolExecutor<br>from concurrent.futures import ThreadPoolExecutor<br>import time<br>import os<br>import random<br><br>def task(name):<br> print(name)<br> print(f'{os.getpid()} 准备接客')<br> time.sleep(random.randint(1,3))<br><br><br>if __name__ == '__main__':<br> p = ProcessPoolExecutor() # 设置进程数量默认为cpu个数<br> for i in range(23):<br> p.submit(task,1) # 给进程池放任务,传参<br><br><br>def task(name):<br> print(name)<br> print(f'{os.getpid()} 准备接客')<br> time.sleep(random.randint(1,3))<br><br><br>if __name__ == '__main__':<br> p = ThreadPoolExecutor() # ,默认cpu数量*5<br> for i in range(23):<br> p.submit(task,1) # 给线程池放任务,传参
同步,异步,携程
阻塞,非阻塞
1.1进程或线程在运行中表现的状态:<br>①阻塞<br>②运行<br>③就绪<br>1.2阻塞:<br>进程或线程遇到IO阻塞. 程序遇到IO立马会停止(挂起), cpu马上切换,等到IO<br>结束之后,在执行.<br>1.3非阻塞:<br>进程或线程没有IO或者 遇到IO通过某种手段让cpu去执行其他的任务,尽可<br>能的占用cpu.
异步,同步
站在任务发布的角度.<br>2.1同步<br>可以从两个方面去看:<br>①进程或线程间存在间接地相互制约关系<br>例如有一台打印机,进程A获取这台打印机后,进程B就会被阻塞,必须等待进程<br>A释放打印机后,进程B才能进入就绪状态,等待CPU执行.<br>②进程或线程间存在直接相互制约关系<br>这种情况源于进程或线程之间存在合作关系.<br>例如进程A通过单缓冲向进程B发送消息,当缓冲区为空时,进程B因获取不到<br>所需的数据而被阻塞,只有当进程A想缓冲区发送数据使缓冲区不为空时,进程<br>B才被唤醒;反过来,当缓冲区满时,进程A因无法向缓冲区放置数据而被阻塞,<br>只有当进程B从缓冲区接收数据,使缓冲区不满时,进程A才被唤醒.<br>结论:同步与阻塞在某些方面可以等价,如果两个进程构成同步关系,如果其中<br>一个进程不能正常工作或者被某些原因阻塞住,那么另一个进程也会迈向阻塞<br>的道路<br>2.2异步:<br>异步方式不用阻塞当前进程或线程来等待结果返回,而是允许后续操作,直<br>至其它进程或线程处理完毕后返回结果,然后通知此进程或线程去接收结果,<br>从这点来看异步与非阻塞在某些方面可以等价.<br>例如网络爬虫爬取图片时,线程A为主线程用于执行数据分析和其他的功能,线<br>程B为爬取线程,主要用于爬取网页信息,线程A执行过程中不会因为未接收到<br>线程B发送的网页信息而被阻塞,当线程B爬取完网页信息后会发送一个消息<br>通知线程A去进行数据分析.
异步+ 调用机制
利用for循环与函数调用发出任务
协程的初识
一个线程实现并发.<br>并发,并行,串行:<br>串行: 多个任务执行时,第一个任务从开始执行,遇到了IO等待,等待IO阻塞结<br>束之后,继续执行下一个任务.<br>并行: 多核,多个线程或者进程同时执行. 4个cpu,同时执行4个任务.<br>并发: 多个任务看起来是同时执行, cpu在多个任务之间来回切换(遇到IO阻<br>塞,计算密集型执行时间过长).
并发的本质:
1. 遇到IO阻塞,计算密集型执行时间过长 切换.<br>2. 保持原来的状态.<br> 一个线程实现并发.<br> 多进程: 操作系统控制 多个进程的多个任务切换 + 保持状态.<br> 多线程: 操作系统控制 多个线程的多个任务切换 + 保持状态.<br> 协程: 程序控制 一个线程的多个任务的切换以及保持状态.<br> 协程: 微并发, 处理任务不宜过多.<br> 协程它会调度cpu,如果协程管控的任务中,遇到阻塞,它会快速的(比操作系统<br> 快)切换到另一个任务,并且能将上一个任务挂起(保持状态,),让操作系统以为<br> cpu一直在工作.<br> 之前我们学过协程?yield 就是一个协程,<br> yield 虽然可以实现两个任务来回切换,并且能够保存原来的状态,而且还是一<br> 个线程,但是 他只能遇到yield切换,遇到Io还是阻塞.<br> 计算密集型:串行与协程的效率对比
协程的优点
多线程并发: 一个进程如果要是开4个线程,最多可以处理30个任务.<br> 多协程并发: 一个进程开启4个线程,然后我将4个线程设置4个协程,每个协程
#1. 协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而<br> 更加轻量级<br> #2. 单线程内就可以实现并发的效果,最大限度地利用cpu<br> #3. 修改共享数据不需加锁
线程与进程与协程的对比总结
进程
特点: 开销大,数据隔离,数据不安全,可以利用多核 操作系统级别,资源分配的<br>最小单位
线程
特点: 开销小,数据共享,数据不安全,可以利用多核 操作系统级别,能被CPU调<br>度的最小单位
协程
特点: 开销小,数据共享,数据安全,不能利用多核,用户级别
分支主题
分支主题
Python基础
基础数据类型
文件操作
f = open("文件",mode="模式",encoding="编码")<br>open() #调用操作系统打开文件<br>mode #对文件的操作方式<br>encoding #文件的编码<br> #不写indocing 默认为系统编码 Windows--gbk<br> #linux&mac ----utf-8<br>f 文件句柄 ---- 操作文件的锅把
### 读 r rb r+ r+b<br>r 读 r+ 读写 #后期开发中使用频率比较低 不能指定编码<br>w写 w+写读 #写读 不能指定编码<br>a写 a+写读 #追加写读 不能指定编码
r
r #文件全部读取完以后,再次读取就没有内容<br>f = open("a",mode="r",encoding="utf-8")<br>f.read() #全部读取<br>c1 = f.read(3) #按照光标读取<br> for i in f:<br> print(i) #一行一行读取--防止内存溢出<br> <br>f.readline() #读取一行 括号加数则读取到本行前几数字个字<br>f.readlines() #读取一行,存储到列表中 /n是换行符<br>f = open(r"F:\\a\,mode="r",encoding="utf-8")<br># r:原生字符显示 \\转义防止\过多需要转义<br> <br># F:\a\111.txt 绝对路径 从磁盘的根处找<br># "111.txt" 相对路径 相对于当前文件的路径进行查找的<br> import os<br> print(os.getcwd()) #查看当前工作路径
rb
f = open("1.jpg",mode="rb") #rb,wb,ab操作没有编码,不能incoding<br>f.read() #括号中加数字则读字节
写 w(清空写) a(追加写)
f = open("day8",mode = "w", coding = "utf-8")<br>f.write("123") # w分为两步 没有文件先创建 然后 清空再重新写 <br>f.close() #关闭文件 关闭文件后就不能继续读取了<br>#操作文件的三步 <br>1.打开文件<br>2.操作文件<br>3.关闭文件<br>f = open("a",mode = "w", coding = "gbk")<br>f.write("啊啊啊,好困啊") #文件末尾添加
wb
f = open(2.jpg,mode="wb")<br>f.weite()<br>#爬虫用
其他操作
tell #查看光标<br>seek #移动光标 文件开始位置<br> seek(0,0) #文件开始位置<br> seek(0,1) #光标当前位置 ---- 废物!!!<br> seek(0,2) #文件末尾位置<br> seek(3) #按照字节调节 使用utf-8是3 使用gbk是2
另一种打开文件的方式
with open("名称",mode="r",encoding="utf-8") as(作为的意思) f:<br> #面向对象中的上下文管理<br> f.read() #会自动关闭文件<br> <br>with open("名称",mode="r",encoding="utf-8") as(作为的意思) f,open("名称",mode="r",encoding="utf-8") as f1:<br> f.read()<br> f1.write("真香") #同时开多个文件
文件的修改
with open("名称",mode="r+",encoding="utf-8") as f:<br> content = r.read()<br> content = replace("A","a")<br> f.seek(0,0)<br> f.write(content)<br><br>with open("名称",mode="r",encoding="utf-8") as f, open("new名称",mode="a",coding="utf-8") as f1:<br> content = i.replace("a","a1")<br> f1.write(content)<br>import os<br>os.rename("名称") #源文件没了<br>os.rename("new名称","名称")
函数
初识函数<br>
函数:函数是以功能为导向,一个函数封装一个功能.登录,注册,文件的改的操作,注册......<br> 优点:减少代码重复性,增强了代码的可读性.
函数的结构
def new_len():<br> count = 0<br> for i in l1:<br> count += 1<br> print(count)<br>new_len()<br>#def(关键字): #定义一个函数<br>#new_len 函数名:与变量命名规范一致 一定要具有可描述性.
函数的调用
def new_len(): #不调用只执行第一行<br> count = 0<br> for i in l1:<br> count += 1<br> print(count)<br> <br>new_len() #函数名() 函数的调用.
函数的返回值<br>
s1 = "asdasd"<br>print(111)<br>print(222)<br>return<br>print(333)<br>print(444)<br>#return 结束函数<br>#给函数的调用者返回具体的值<br>#1.函数中没有return或只写一个return 函数的调用者得到的是None<br>#2.函数中return后面是单个值.数值是什么类型则返回什么类型(不改变值得类型)<br>#3.函数中return后面是多个值,用逗号隔开,返回一个元祖
函数的参数
l1 = [1,2,3]<br>def new_len(xingcan): #定义参数时:参数:形参.<br> count = 0<br> for i in xingcan:<br> count += 1<br> return count #<br>new_len() #函数的调用者:参数 实参.<br>#函数的传参:函数的扩展性
函数的传参
#实参角度<br> 1.位置参数 #从左至右,按照顺序一一对应<br> 2.关键字参数 #按照顺序一一对应<br> 3.混合参数 #混合参数:关键字参数一定要在位置参数后面 一一对应<br>#形参角度<br> 1.位置参数 #与实参角度一样<br> 2.默认参数 #一定在形参的后面,不传参数则沿用默认参数<br> 3.动态参数<br> 4.仅限关键字参数
万能传参
#当给函数传入的参数数目不定时,之前的穿饭餐方式解决不了问题<br>#万能参数//动态参数*args 将实参角度: 定义一个函数时* 所有位置参数聚合到一个元祖中.<br># **kwargs 函数定义是:**讲实参角度所有的关键字参数聚合成一个字典
函数形参角度的形参顺序
位置参数>*args>默认参数>**kwargs
从全局角度研究函数
全局变量:py文件运行时开辟的,存放的是执行的py文件(除去函数内部)的所有的变量与值(地址)的对应的关系,整个py文件结束之后,才会消失<br>临时变量:函数执行时,在内存中临时开辟的一个空间,存放的函数中的变量与值得对应关系,随着函数的执行完成而消失
加载顺序:<br> 内置名称空间先加载---->全局名称空间---->(当函数执行时)临时名称空间<br>取值顺序: (就近原则)
全局作用域:全局名称空间,内置名称空间<br> 局部作用域:局部名称空间
print(globals()) #全局作用域所有的内容<br>print(locals()) #当前位置的变量与值得对应关系
golbal,nonlocal
#golbal 可以在局部作用域生命一个全局变量。可以修改全局变量 剪切<br>#nonlocal 不能操作全局变量 可以对父级作用域的变量进行修改,并且在当前作用域复制一份此变量
默认参数的坑
#当函数的默认参数如果是可变的数据类型,会新建一个内存空间单独存放.
函数名的应用
1.函数名指向的是函数的内存地址.加上()就执行这个函数.<br> 2.函数名是一个变量.可以进行赋值运算<br> 3.函数名可以作为容器类类型的元素.<br> 4.函数名可以作为函数的实参.<br> 5.函数名可以作为函数的返回值.
迭代器
字面意思:可以重复迭代的工具.<br> 专业角度:内部含有"__ iter__" 方法,并且 "__next__" 方法的对象,就是迭代器
l1 = [1,2,3,4,5]<br>obj = iter(l1) l1.__iter__()<br>#迭代器可以迭代取值.利用next()进行取值.<br>print(next(obj)) 1<br>print(next(obj)) 2<br>print(next(obj)) 3<br>print(next(obj)) 4<br>print(next(obj)) 5<br>print(next(obj)) #报错
迭代器优点: 非常节省内存.惰性机制.<br> 缺点:不直观. 不灵活. 效率相对低(用时间换空间) <br> 特性:迭代器是一条路走到黑,不走回头路(记录位置)
可迭代对象与迭代器的对比<br> 可迭代对象:一个操作比较灵活,直观,效率相对高,但是比较占用内存的数据集<br> 迭代器:一个非常节省内存,满足惰性机制,但是效率相对低,操作不灵活的数据集
生成器
生成器本事就是迭代器.Python社区生成器与迭代器是一种.生成器与迭代器的唯一区别: 生成器是我们自己用Python代码构建的.
生成器函数
def func():<br> print(111)<br> yield<br>ret = func() #生成器对象<br>print(ret) #打印生成器地址<br>#只要函数中出现yield ,那么该函数就变成生成器函数<br>#一个next对应一个yield<br>#return 和yield区别<br>return 结束函数 给函数的执行者返回一个值 (如果多个值通过元祖返回)<br>yield 暂停函数 对应着给next返回值 (如果多个值通过元祖返回)<br>yield from <br>#区别: yield :对应next给next返回值<br>yield from 讲一个可迭代对象的每一个元素返回给next 节省代码,提升效率(代替了for循环)
列表推导式,生成器表达式
列表推导式:一行代码构建一个有规律比较复杂的列表
l1 = [i for i in range(1,101)]<br>print(l1)<br>#两种构建方式:<br>1,循环模式:[变量(加工后的变量) for 变量 in iterable]<br>2.筛选模式:[变量(加工后的变量) for 变量 in iterable if 条件]
列表推导式的优缺点:<br> 优点:简单,快捷,装逼<br> 缺点:可读性不高 不好排错,慎用 不要入迷
### 生成器表达式<br>与列表推导式几乎一模一样
循环模式<br>obj = (i for i in range(1,11))<br>print(next(obj))<br>#如何触发生成器(迭代器)取值<br>1:next 2:for循环 for i in obj: print(i) <br> 3:数据转换 print(list(obj))<br>#生成器表达式:节省内存<br>筛选模式<br>obj = (i fir i in range(1,11) if i > 10)<br>print(next(obj))
匿名函数<br>
#匿名函数只能构建简单的函数,一句话函数
def func(x,y)<br> return x + y<br>print(1,2)<br>#匿名函数的构建<br>func2 = lambda x,y:x + y<br>print(func(1,2))
#匿名函数最长用的就是与内置函数结合使用
内置函数
eval exce
解开包裹
hash:获取一个对象(可哈希对象:int,str,Bool,tuple)的哈希值。
help:函数用于查看函数或模块用途的详细说明。
callable:函数用于检查一个对象是否是可调用的。如果返回True,object仍然可能调用失败;但如果返回False,调用对象ojbect绝对不会成功。
int:函数用于将一个字符串或数字转换为整型
complex:函数用于创建一个值为 real + imag * j 的复数或者转化一个字符串或数为复数。如果第一个参数为字符串,则不需要指定第二个参数。
bin:将十进制转换成二进制并返回。<br>oct:将十进制转化成八进制字符串并返回。<br>hex:将十进制转化成十六进制字符串并返回。
divmod:计算除数与被除数的结果,返回一个包含商和余数的元组(a // b, a % b)。<br>round:保留浮点数的小数位数,默认保留整数。<br>pow:求x ** y次幂。(三个参数为x**y的结果对z取余)
bytes:用于不同编码之间的转化。
ord:输入字符找该字符编码的位置<br>chr:输入位置数字找出其对应的字符
repr:返回一个对象的string形式(原形毕露)
all:可迭代对象中,全都是True才是True<br>any:可迭代对象中,有一个True 就是True
print() 屏幕输出
dict() 通过相应的方式创建字典。
abs() 返回绝对值
sum() 求和
min() 求最小值
max() 最大值与最小值用法相同。<br>reversed() 将一个序列翻转, 返回翻转序列的迭代器 reversed
bytes() 把字符串转换成bytes类型
reversed() 将一个序列翻转, 返回翻转序列的迭代器 reversed 示例:
zip() 拉链方法。函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,<br>然后返回由这些元祖组成的内容,如果各个迭代器的元素个数不一致,则按照长度最短的返回,
sorted**排序函数
filter**筛选过滤
map映射函数
闭包
#判断一个函数是不是闭包====闭包函数有没有自由变量<br># 函数名.__code__.co_freevars 查看函数的自由变量<br>print(avg.__code__.co_freevars) # ('series',)<br># 函数名.__code__.co_varnames 查看函数的局部变量<br>print(avg.__code__.co_varnames) # ('new_value', 'total')<br># 函数名.__closure__ 获取具体的自由变量对象,也就是cell对象。<br># (<cell at 0x0000020070CB7618: int object at 0x000000005CA08090>,)<br># cell_contents 自由变量具体的值<br>print(avg.__closure__[0].cell_contents) # []
**闭包的应用**:<br>1. 可以保存一些非全局变量但是不易被销毁、改变的数据。<br>2. 装饰器
开放封闭原则
软件面试时,不可能把所有的功能都设计好,当前的未来一两年功能上线,定期更新迭代,对于软件之前的写得源代码一般都不会修改.对函数里面的代码以及函数的调用方式<br> 开放原则:在源码不改变的情况下,增加一些额外的功能<br> 封闭原则:不改变源码 不改变函数的调用方式<br> Python装饰器:完美的全是的开放封闭原则<br> 装饰器就是一个函数,他要装饰一个函数,在不改变原函数的源码以及调用方式的前提下,给其增加一个额外的功能.
装饰器
def warpper(f):<br> def inner(*args,**kwargs):<br> # 被装饰函数之前的操作<br> print(666)<br> ret = f(*args,**kwargs)<br> print("执行完毕了")<br> # 被装饰函数之后的操作<br> return ret<br> return inner<br><br>@warpper<br>def func():<br> print (111)
装饰器的应用:在不改变原函数的源码以及调用方式前提下,为其增加额外的功能.<br> 登录,认证,打印日志等
递归函数
递归函数:函数或者其他代码都可以解决递归解决的问题.但是递归在某些时候能有出奇制胜的效果.人理解函数,神理解递归.
count = 0<br>def func():<br> global count<br> count += 1<br> print(count)<br> func()<br>func()
官网规定:默认递归的最大深度1000次.<br> 如果递归超多百次还没解决,那么执意使用递归,效率会很低<br>
概念
小数据池
小数据池是Python中一种提高效率的方式,固定数据类型使用同一个内存地址<br>小数据池----支持 str,int,bool
#id 查看空间的内存地址 获取开辟控件的一种地址<br>id(a)== id(b)<br>#数字范围 -5~256
代码块:一个文件,一个模块,一个函数,一个类,终端中每一行都算一个代码块<br>在Python中是用字典的方式存储<br>is #判断两个内存地址是否相同<br>== #判断等号两边的值是否相同 <br>#如果 is判断为True 那么 == 一定为True
字符串:<br> 1.字符串在做乘法的时候总长度不能超过20,进行驻留<br> 2.自己定义的字符串 长度不限制 字符串必须由数字,字母,下划线组成,进行驻留<br> 3.特殊字符(中文除外)定义1个时候,进行驻留<br> 4.字符串*1 其实就是赋值
代码块
数字:全部驻留
字符串:<br> 1.字符串做乘法的时候总长度不能超过20<br> 2.自己定义的都驻留<br> 3.乘数为1 就是赋值<br> 4.Python3.7中乘法长度改为不超过4096
小数据池数字:-5~256 !!!<br> 小数据池字符串:乘法的时候总长度不能超过20 !!!<br> 小数据池----支持 str,int,bool
#怎么自己驻留?<br>#使用Python内置的模块<br>from sys import intern<br>a = intern("@@@@@@" * 20)<br>b = intern("@@@@@@" * 20)<br>结果为True
深浅拷贝
深拷贝
import copy<br>li = [1,2,3,4,5,[6,7,8]]<br>l2 = copy.deepcopy()<br>print(li)>>>>[1, 2, 3, 4, 5, [6, 7, 8]]<br>print(id(li))>>>>1420229662280<br>print(id(li[0]))>>>>1387364464<br>print(l2)>>>>[1, 2, 3, 4, 5, [6, 7, 8]]<br>print(id(l2))>>>>1420229663560<br>print(id(l2[0]))>>>>1387364464<br>#深拷贝 ---- 不可变的数据类型和原列表指向同一个内存空间<br># 可变数据类型,会创建一个新的空间
## 深浅拷贝在哪用<br> 1.不修改原数据的基础上进行操作
浅拷贝
l1 = [1,2,3,4[5,6,7]]<br>l2 = l1[:] #浅拷贝<br>l2 = l1.copy() #浅拷贝----只拷贝第一层元素<br>#浅拷贝会创建一个新的列表(容器),列表中的元素和原列表共用一个内存空间
## 总结<br>赋值:多个变量指向同一个内存地址<br>浅拷贝:只拷贝第一层元素,可变元素不动指向原先位置<br>深拷贝:全部拷贝 ---- 不可变数据类型指向的是原数据的内存地址<br> -----可变数据类型会创建一个新的空间<br>#### 深浅拷贝的规律:<br> 赋值: 两个变量使用的是同一个空间<br> 浅拷贝:修改不可变数据类型,不变动,修改可变数据类型,变动<br> 深拷贝:修改就不变动
赋值
l1 = [1,2,3,4[5,6,7]]<br>l2 = l1 #赋值
基础数据类型
Python的种类
Cpython
子主题
Jyhton
IronPython
PyPy
Cpython
子主题
变量的命名规范
字母 数字 下划线组成
不能以数字开头
不能使用python的关键字
不能使用中文和拼音
区分大小写
变量名要具有意义
推荐写法,驼峰体,首字母大写
流程控制语句
if 条件:
多选一<br>if 条件:<br> 结果<br>elif 条件:<br> 结果<br>elif 条件:<br> 结果
多选<br>if 条件:<br> 结果<br>if 条件:<br> 结果<br>if 条件:<br> 结果
if嵌套
if 条件:
if 条件:<br> 结果<br>else:<br> 结果
字符串格式化
%s ----占字符串的位置
子主题
%d ----占整型的位置
%% ----转义(把占位转换成普通的%号)
f 字符串拼接 ----3.6版本以上
while循环
while 关键字 条件: (死循环)
break 终止当前的循环
continue 跳出本次循环,继续下次循环 伪装成循环体中最后一行
运算符
算数运算符<br>/ % ** // + - *
赋值运算符<br>= += -= *= /= **= %= //=
逻辑运算符<br>and(与 -- 和) or (或) not (非 -- 不是)
比较运算符<br>== != > < >= <=
成员运算符<br>in 在 not in 不在
编码
ASCII码 -- 不支持中文
GBK -- 国标:<br>英文 1个字节<br>中文 2个字节
Unicode -- 万国码:<br>英文 2个字节<br>中文 4个字节
utf-8 -- 最流行的编码方式<br>英文 1个字节<br>欧洲 2个字节<br>亚洲 3个字节
单位转换
1B = 8b
1024B = 1KB
1024KB = 1MB
1024MB = 1GB
整数int<br>
a.bit_length() #求十进制数转换为二进制时所占用的位数
布尔值 bool
True 真 False 假
数字转成布尔值: 0 False 非0 True<br>
字符串转换成布尔值: 空字符串 "" False 非空 True<br>
布尔值转换成数字 True = 1 False = 0<br>
布尔值转换成字符串 True = str(True) False = str(False)
字符串
索引
从左向右 0, 1, 2, 3----从右向左 -1, -2, -3, -4<br>(查找时超出范围会报错)<br>
切片
:[起始位置:终止为止] 顾头不顾尾 查找的时候超出范围没事
步长
决定查找的方向 决定查找的步子大小 默认为1 默认方向从左向右
字符串 列表 元祖----都是有索引 [下标]<br>索引是准确的定位某个元素<br>支持索引的都支持切片 [索引]<br>切片长度可以超出范围,索引长度不能超出范围 [起始位置:终止为止:步长]<br>步长:决定要走的方向,决定走的步子多大
常用方法<br>
全部大写upper
全部小写lower
以什么开头 startswith
以什么结尾 endswith
去头尾空格 (默认去掉去掉和换行符) strip
分割 默认是空格分割,可以自己指定 split
替换 replace("旧的","新的")
统计 count 统计某个内容出现的次数
字符串格式化 format() 三种使用方式
Python新特性,f-string<br> 1.不区分大小写<br> 2.可以加入表达式<br> 3.可以结合函数<br> 4.不能放特殊的字符 : ; ! {}
isdecimal() 判断是否为十进制
isalnum 判断的是不是中文,字母,数字
isalpha 判断的是不是中文,字母
补充方法
<br>
s1 = str(“abc”) #类型转换
s.capitalize() #首字母大写
s.title() #每个单词首字母大写
s.count() #统计出现的次数
s.find() #查找 查找不到的时候返回-1
s.index() #查找 找不到就报错
for循环
for 关键字 i 变量 in 关键字 xxx可迭代对象(不能用int或bool)
循环后的i变量指向的是最后的一个元素
占位符: pass或...
range
python3中打印的是range本身
range(起始位置,终止为止,步长) 逗号相连<br>range(10) ---- range() 起始位置0 终止为止10
列表<br>
列表支持索引,切片,步长<br>列表是可变的
增
li.append("太亮")<br>li.insert() #插入 (不顾头,索引,要插入的内容) 效率特别低<br>li.extend("abc") == li[1,2,3,"alex", True, [1,2,3],"a","b","c"] #迭代添加 <br> for i in "abc"<br> li.append(i) #迭代添加结束
删
li.pop() #弹 <br>li.remove(3)>>>>li = [[1,2,"abc",True]] #删除 #按照元素名进行删除<br>del li[3] #Python关键字<br>li.clear()
改
li[0] = "奶瓶"<br>li[1:3] = l2[4:]<br>li[1:2] = [1,2,3] #取代 覆盖<br>#索引 切片 步长(步长取几个取代他的就是几个)
查
li = [1,2,3,4]<br>print(li)<br>for em in li:<br> print(em)
列表的嵌套
print(li[6][2][0]) = xxx<br>
补充方法
li.count() #统计
li.index() #查看
li.reverse() #反转
li.sort() #排序 升序
li.sort(reverse = True) #降序
元组
tuple tu = (1,2,3) #每个元素都以逗号隔开 (1)是整型;;;;(1,) 是元祖<br>元祖是不可变的数据类型 元祖只能查看不能修改 ------元祖和列表相似<br>存储一些不像让别人修改的数据 -- 容器 存储任意数据类型<br>使用在配置文件中,程序中 为了防止误操作修改一些数据
tu = (1,2,3)#索引<br>tu[1] (2)#切片<br>tu[1:3] (2,3) #切片返回的是原先的数据类型#步长<br>tu[::3] (1,3)
tu = (1,2,3(4,5,6[6,7,8(9,11,10),"abc"],"ABC"))<br>tu[3][3][3][1] = 11<br>tu[-1][-2][-1] = abc<br>
补充方法
tu.count("3") #统计
tu,index("1") #查找
字典
dict 关键字<br>{} "键":"值" 别的语言叫:键值对数据<br>dic = {"key":"value",1:2,2:3}<br>键:必须是可哈希的(不可变的数据类型),并且是唯一的<br>值:任意的<br>
增
dic.setdefault("c","aaa") #先去字典中查看要添加的键是否存在,如果存在则不添加,如果不存在则添加.<br>dic["s"] = "ssss" #dic[键] = 值
删
dic.pop("a") >>>> dic = {"c":"d"} #通过键删除;有返回值,返回的是被删除的值<br>#字典没有remove<br>del dic["b"] >>>> dic = {"a":"b"} #删除指定的键值对<br>del dic #删除整个字典<br>dic.clear #清空
#字典的分查询只能通过键获取值,不能通过值获取键<br>dic["a"] #如果键在字典中存在,就返回键对应的值,如果不存则报错 #get ----获取<br>dic.get("a","定义返回内容")>>>> a1 #键不存在的时候不报错,可以指定返回的内容 推荐使用<br>for i in dic:<br> print(i) #for循环字典的时候打印的是字典的键<br> print(i,dic[i]) #打印键和值<br>dic.setdefault("键")
#解构<br>a = 1<br>b = 2<br>a,b = b,a 交换内存地址的指向<br>a,b = 1,2<br>a,b = (45,6)<br>a,b = [1,4]<br>a,b = {"s":1,"a":1} #字典拆包后的结果是键赋值给了a和b的变量
for i ,k in enumerate(dic,0) #第二个是参数 ----枚举方法<br> print(i,k)<br>>>> 0 1<br> 1 2<br> 2 3<br> 4 5
查
#使用键寻找内容<br>#查找字典的嵌套时,按照键去一层一层的查找
改
dic["b"] = "这是值" #键在字典中存在则修改 键在字典中不存在就是增加(同增加)<br>dic.update({1:2,"a":"a1"})>>>>dic = {"a":"a1","c":"d",1:2} #update括号里的字典级别高
其他操作
dic.keys(): #高仿列表 没有索引<br>dic.valuie() #高仿列表 没有索引<br>dic.items() #高仿列表 列表里存放的是元祖类型,元祖第一个是键,第二个是值<br>for key,value in dic.tiems():<br>print(key,value)<br>>>> 1 1<br> 2 2<br> 3 3<br> 4 4
## 字典能干啥<br>1.字典是存储大量的数据,字典比列表存储的还要多<br>2.字典在查找值的时候能够方便快速<br>3.能够将数据进行关联<br>4.字典比较消耗内存 最常用的数据:字符串,列表,字典
补充方法 <br>
dic = dict(k=1,k=2)
dic.popitem() #随机删除 3.6版本以上 默认删除最后一个键值对
dic.fromkeys("123",1) #批量创建<br>#第一个参数是可迭代对象<br>#第二个参数是每个键对应的值 ----用的都是同一个内存地址
集合
Python数据类型之一
s = {1,2,3,4,"123",True,False,(1,2,3,4)}<br>print(s)>>>> {False, 1, 2, 3, 4, '123', (1, 2, 3, 4)}<br>#集合就是一个没有值的字典,遵循唯一,无序,元素要求可哈希(不可变)<br>#集合是可变的<br>#集合天然去重
增:
s = {1,2,3,4,"123",True,False,(1,2,3,4)}<br>s.update("456") #迭代添加<br>s.add("啊啊啊") #添加
删:
s.pop() #删第一个<br>s.remove() #指定删除<br>del s #删除整个集合<br>s.clear #清空
改:
#删了重新添加
查:
for i in xxx
其他操作
s1 - s2 = {1, 2} #差集<br>s1 | s2 = {1,2,3,4,5,6} #合集<br>s1 & s2 = {3,4} #交集<br>s1 ^ s2 = {1, 2, 5, 6} #反向交集&对称差集<br>s1 > s2 = False #超集 ----父集<br>s1 < s2 = False #子集<br>#冻结集合(可变 转换为 不可变) ----不常用<br>frozenset ({1,2,3,4,5})<br>s = set("1234") #定义方式
数据类型转换
str>>>int
n = int(s)
int>>>str
s = str(n)
str>>>list
li.list(s) #转列表
s.split(s) #转列表
s.join(li) #转列表 join中不能有整型int
list>>>tuple
tu = tuple(li)
set>>>list
list(s)
list>>>set
s = set(li)
字符串----数字:字符串中必有都是十进制的数字<br> 数字----字符串:直接转<br> 列表----字符串:''.join()<br> 字符串----列表:split<br> 除去字典外,容器数据类型之间可以直接相互转换
str,int,bool,list,tuple,dict,set<br> 有序:str,int,bool,list,tuple<br> 无序:dict,set<br> 可变:list,dict,set<br> 不变:str,int,bool,tuple<br> 访问方式:<br> 直接访问:int,bool,set<br> 顺序访问:list,tuple,str<br> 键访问:dict
各种坑
1. 删除列表<br> 使用for删除列表的时候从左往右删会报错,因为索引值会改变<br>2. 字典for的时候怎么删除键值对<br> 不能在遍历字典本身中改变大小,要批量删除字典的键值对
使用for循环列表的时候,因为列表会自动补位,和for自己维护了一个遍历计数器 删不干净<br>解决"倒序删除或 创建一个新列表 存放要删除的元素,最后for新列表,删除原列表的元素<br>for循环列表的时候,添加元素,会产生什么效果? 死循环<br>字典:字典遍历的时候不能修改字典本身的大小<br>新建一个列表,将要删除的键,存储 然后for新列表,删除字典中的键值对
编码
s = “今天晚上,吃鸡!”<br>s1 = s.decode("utf-8") #编码<br>s1 = s.encode("utf-8") #解码<br>#Python3内存中使用的就是unicode<br>#Python2中内存使用的就是ASCII
分支主题
分支主题
0 条评论
下一页