前言
将之前的Python文章修改、扩展,并整合到一起,方便查阅。
一、python胶水语言
计算机语言类型
编译型语言:将源代码编译生成机器语言,再由机器运行机器码(二进制)。
解释性语言:在运行的时候将程序翻译成机器语言。
从定义上来看,就知道编译型语言的运行速度,要比解释性语言快!那么,解释性语言是否就没那么“可口”了?
解释性语言的优点
1、不需要编译器(以前学C语言的时候,总觉得这个比较麻烦)
2、兼容性更好(如:python可以在多个系统使用,无需重新编译)
3、语法相对简练(越是接近机器语言,语法越难理解,如汇编语言)
python语言
1、语言还可以分为强类型、弱类型:
(1)强类型定义语言:强制数据类型定义的语言,即使用前需要定义类型,该类型正常情况下不允许改变,如:C语言的整型变量定义(init i),必须要定义才能使用。
(2)弱类型定义语言:数据类型可以被忽略的语言,即使用前无需定义,数据类型根据复制内容变化而变化。如python语言的某个变量定义(a=2 ;a=”str”),前一秒还是整型,下一秒成字符串了。
从上面的定义,我们可以看到,代码的维护,强类型语言更好一些;编码的时候,弱类型语言编码更自由些
2、python到底是啥?
python是一门弱类型定义的解释性语言。
它是一种跨平台的计算机程序设计语言。 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。最初被设计用于编写自动化脚本shell),随着版本的不断更新和语言新功能的添加,越多被用于独立的、大型项目的开发。
俗称:胶水语言。
3、为什么要学python
我说有趣,你会相信吗?
二、python面向对象编程
面向对象语言
面向对象语言(Object-Oriented Language)是一类以对象作为基本程序结构单位的程序设计语言,指用于描述的设计是以对象为核心,而对象是程序运行时刻的基本成分。语言中提供了类、继承等成分,有识认性、多态性、类别性和继承性四个主要特点。
python具备这些特点,所以它是面向对象语言。
面向对象编程
面向对象程序设计Object Oriented Programming)作为一种新方法,其本质是以建立模型体现出来的抽象思维过程和面向对象的方法。模型是用来反映现实世界中事物特征的。任何一个模型都不可能反映客观事物的一切具体特征,只能对事物特征和变化规律的一种抽象,且在它所涉及的范围内更普遍、更集中、更深刻地描述客体的特征。通过建立模型而达到的抽象是人们对客体认识的深化。
python的程序设计,以这种方式进行,叫面向对象编程。
python面向对象编程
举个例子:
class animalobject):class dogobject): def jinmao):print"金毛狗")class fishobject): def jinqiangyu):print"金枪鱼")def luofeiyu):print"罗非鱼")animal.dog.jinmao)
如上,使用class定义了一个类,并继承了object。animal是一个大类,很明显是动物的意思,具有识认性;动物有很多种,具有类别性;多态性如何理解?对于金枪鱼,你可以叫它金枪鱼,也可以叫它鱼,还可以叫它动物。具有多种形态的叫法(不同功能),称之为多态。
这就是面向对象编程。
面向对象编程的做法是,当解决一个问题的时候,面向对象会把事物抽象成对象的概念,就是说这个问题里面有哪些对象,然后给对象赋一些属性和方法,然后让每个对象去执行自己的方法,问题得到解决。
在面向对象编程之前,面向的是过程,如C语言。面向过程注重过程的。当解决一个问题的时候,面向过程会把事情拆分成: 一个个函数和数据(用于方法的参数) 。然后按照一定的顺序,执行完这些方法(每个方法看作一个个过程),等方法执行完了,事情就搞定了。
三、python编码规范
python编码规范
官方网址:PEP 8 – Style Guide for Python Code | peps.python.org
官方介绍如下:
本文档给出了Python主要发行版中包含标准库的Python代码的编码约定。请参阅有关Python[1]的C实现中的C代码的描述样式指南的配套信息PEP。
本文档和PEP 257文档字符串约定)改编自Guido的原始Python风格指南文章,并添加了一些Barry的风格指南[2]。
这种风格指南随着时间的推移而发展,因为语言本身的变化确定了更多的约定,而过去的约定也变得过时了。
许多项目都有自己的编码风格指南。在发生任何冲突时,此类特定于项目的指南应优先用于该项目。
那么,本文是简单介绍PEP 8和PEP 257介绍。
pep8规范
格式化pep8快捷键:ctrl+alt+L
1、缩进:每一级缩进4个空格(pycharm里是一个tab)
对齐缩进:参数对齐左边括号
悬挂缩进:4个空格,对齐参数
层级缩进:8个空格,对齐参数
2、行的最大长度
单行代码字符数不要超过79个字符
通过“\”来隐式换行
注释最大72字符
with openr"D:\360downloads\wpcache\p3.qhimg.com\bdr\__85\file01") as file01, \openr"D:\360downloads\wpcache\p3.qhimg.com\bdr\__85\file02") as file02:content=file01.read)file02.writecontent)
3、空行
顶层函数和类定义,前后用两个空行隔开
类里的方法定义用一个空行隔开
4、注释
写代码前先写注释,修改代码也应该先修改注释
def funcn1, n2, n3 ):"""自定义函数:n1:参数1:n1:参数2:n1:参数3:return:返回结果"""
5、命名约定
禁止使用I(小写的L)、O(大写的o)、l(大写的i)
6、字符串引号
7、模块和包导入规范
模块导入顺序:官方库-》第三方库-》自定义模块
all变量:限制导入可以使用这种方法
pep257规范
官方文档:PEP 257 – Docstring Conventions | peps.python.org
1、工程结构化
项目名称,即项目文件夹
四、python变量赋值
普通赋值
a = 1
这是python赋值的基本语法,后面的赋值都是基于此。
元组赋值
a, b = 1, 2a, b = 1, 2)a, b) = 1, 2)
列表赋值
[a, b] = [1, 2]a,b = [1 , 2]
序列赋值
a, b = "12"a, *b = "1234"
*表达式用于分解序列时,*b中的b是一个列表,语法为: … ,*b ,…=序列 ;
*表达式用于解包时,*b会将列表元素直接解包,作为参数直接传入,语法为 def func*b)。
多目标赋值
a = b = c = 1
增强赋值
a = 0a += 1
五、python内置函数
内置函数
嵌入到主调函数中的函数称为内置函数,又称内嵌函数、内建函数。 作用是提高程序的执行效率。大多编程语言都有自己的内置函数,如C语言的isupper‘a’)用于判断字母大小,输出布尔值。 内置函数的存在极大的提升了程序员的效率和程序的阅读。
python也有自己的内置函数,而且丰富多彩。
查看内置函数
1、dir
dir是一个内置函数,用法如下:
dir__builtins__)
如图,使用该方法即可查看python自身的内置函数。可以看到,dir也是内置函数,作用是可以查看对象内的所有的属性和方法。如查看一个模块sys:
import sysdirsys)
如图,通过dirsys)可以查看该模块的方法和属性。
那么如何区分哪些是属性、哪些是方法、哪些是类呢?
[name,typegetattrmath,name))) for name in dirmath)]
其中,getattr和type也是内建函数。
2、getattr
内置函数getattr可以根据属性名称返回对象值。如果“name”是对对象属性的名称,则返回对应属性的值。使用方式:getattrobject, name[, default])
3、type
内置函数type可以查看对象类型,用于判断对象是否为一个函数、列表、字典等等
4、其他
请使用内置函数help)查看对象的用法,如:
5.1 python内置函数open)详解
1、查看帮助文档
helpopen)
2、基本操作
打开一个数据集压缩文件ant_bees_data.tar.gz:
open"D:/360MoveData/Users/lenovo/Desktop/test/ant_bees_data.tar.gz", "rb")
打开文件成功,返回一个文件对象。
如文档所示,open) 的第一个参数是文件路径,第二个参数是模式,“rb”中的r表示只读方式打开,b表示以二进制方式打开。不同方式打开,会对文件施加不同作用,所以,需要根据实际情况选择模式,避免误操作,部分参数如下:
参数 |
施加作用 |
w |
写的方式:文件存在则覆盖,不存在则创建 |
r |
读的方式:文件不存在即报错 |
a |
追加的方式:新写入的内容在最末尾,若文件不存在则创建 |
x |
创建的方式:文件存在则报错,不存在则创建 |
w+代表读写,r+代表读写,a+代表读写,三者有什么区别呢?
区别在于施加作用,如w+,它具有w的施加作用,而不具备r的施加作用。
如果不加参数,默认“rt”方式打开,即文本只读。
打开文件后,文件会以文件流的方式加载到内存中。流是个很有意思的词汇,中华文化的完美体现。(一直觉得流是写意的存在,贬义词除外):
流是用于处理网络连接的高级 async/await-ready 原语。流允许发送和接收数据,而不需要使用回调或低级协议和传输。
流,简单来说就是建立在面向对象基础上的一种抽象的处理数据的工具。在流中,定义了一些处理数据的基本操作,如读取数据,写入数据等,程序员是对流进行所有操作的,而不用关心流的另一头数据的真正流向。流不但可以处理文件,还可以处理动态内存、网络数据等多种数据形式。
3、对象
open)返回一个文件对象,其类型取决于模式。
如图,Buffered是不一样的,表明生成的缓存不一样,即“流派”不一样,操作也不一样。就好比武当派打的是太极,青城派使用的是暗器。
对象实例:
如图,在相同的打开方式,文件加载后,也是不同的实例。
4、如何优雅的open
通常我们打开文件后,是需要关闭的,关闭的方式是调用对象的close方法,如:
f = open"D:/360MoveData/Users/lenovo/Desktop/test/ant_bees_data002.tar.gz", "rb")
f.name
f.close)
等价于:
with open"D:/360MoveData/Users/lenovo/Desktop/test/ant_bees_data002.tar.gz", "rb") as f:f.name
上面的方式是python的上下文管理方法,通过该方式,自动调用了close)
5、实战:大文件分块与合并
(1)分块
import osimport math# 文件路径file_path = "D:/360MoveData/Users/lenovo/Desktop/test/ant_bees_data.tar.gz"# 读取文件大小,单位为字节Bfile_total_size = os.path.getsizefile_path)printfile_total_size)# 设置分块大小,每块为5M(1MB=1024KB,1KB=1024B)chunk_size = 1024*1024*5# 分块数量chunks = math.ceilfile_total_size/chunk_size)printchunks)# 切割文件,获取每一块文件,并保存for chunk in rangechunks):print'chunk', chunk)if chunk+1 == chunks:is_end = 1current_size = file_total_size % chunk_sizeelse:is_end = 0current_size = chunk_sizeoffset=chunk*chunk_size # 偏移量# 获取分块并保存with openfile_path, "rb") as targetFile:# 获取偏移量targetFile.seekoffset) # 从偏移量开始切割# 保存分块为新文件with open"D:/360MoveData/Users/lenovo/Desktop/test/ant_bees_data{}.tar.gz".formatchunk), "wb" ) as chunkfiles:chunkfiles.writetargetFile.readcurrent_size)) # 从偏移量开始,读current_size以获得该分块,并保存
(2)合并
将上面的分块,合并成一个新文件:
import osfilepath = "D:/360MoveData/Users/lenovo/Desktop/test/onefile.tar.gz" # 合并后的新文件chunk = 0task ="ant_bees_data{}.tar.gz" # 根据切块的规律设计占位batchs_dir = "D:/360MoveData/Users/lenovo/Desktop/test/" # 分块文件地址with openfilepath, 'wb') as target_file:while True:try:filename=os.path.joinbatchs_dir,task.formatchunk))# 读取文件后进行保存with openfilename, 'rb') as source_file:target_file.writesource_file.read))except :breakchunk += 1
6、实战:分块传输大文件,避免客户端奔溃
通过上面,我们学会了如何分块,现在,我们运用于接口测试,通过requests进行分块请求,该流程为:调用login)方法登录系统,获得token;每切一块文件,就通过postChunks)方法上传该文件;上传文件后,调用addData)方法,请求后台接口合并文件,从而实现分块上传,后台合并的业务需求。
import osimport mathimport jsonimport requestsclass saveDataobject):passdef login):data={}# 登录用户url = "http://10.12.1.153:1027/logintp"data["user"] = "abc001"data["pwd"] = "abc001"res = requests.request"POST", url=url, data=data)setattrsaveData, "{}".formatdata["user"]), json.loadsres.text)["data"]["token"])def postChunkschunks, chunk, file_path, offset, current_size):url = "http://10.12.1.153:1027/file/upload"payload = {"task_id": "task_id006","chunks": strchunks),"chunk": strchunk)}targetFile = openfile_path, "rb")targetFile.seekoffset)files = ["file", targetFile.readcurrent_size))]headers = {}headers["Authorization"] = "Bearer " + getattrsaveData, "abc001")response = requests.request"POST", url, headers=headers, data=payload, files=files)printresponse.text)def addData):url = "http://10.12.1.153:1027/file/merge"payload = {'task_id': 'task_id006','filename': '10G_ant_bees_data.zip','user': "abc001",'isoriginal': '1','scene': '2','batchname': 'test_id006'}headers = {}headers["Authorization"] = "Bearer " + getattrsaveData, "abc001")response = requests.request"POST", url, headers=headers, data=payload)printresponse.text)# 文件路径file_path = "E:/training训练数据/10G_ant_bees_data.zip"# 读取文件大小,单位为字节Bfile_total_size = os.path.getsizefile_path)printfile_total_size)# 设置分块大小,每块为5M(1MB=1024KB,1KB=1024B)chunk_size = 1024*1024*5# 分块数量chunks = math.ceilfile_total_size/chunk_size)printchunks)# 登录,获取tokenlogin)# printgetattrsaveData, "abc001"))# 每块发送一次请求for chunk in rangechunks):print'chunk', chunk)if chunk+1 == chunks:is_end = 1current_size = file_total_size % chunk_sizeelse:is_end = 0current_size = chunk_size# 发送一次请求postChunkschunks=chunks, chunk=chunk, file_path=file_path, offset=chunk*chunk_size, current_size=current_size)# 新增数据集addData)
5.2 python内置函数eval)详解
(1)功能1:作用于字符串,让其解析成表达式
x = 1printeval'x+1'))
(2)功能2:解析字符串类型的字典、列表为python对象
@字典:
a = "{'a': 'a', 'b': 'b'}"printtypeevala)))
@列表
a = "[1,2,3,4]"b = evala)printtypeb))
(3)eval)中的gloabals和locals
x = 10 # 全局变量 gloabalsdef fun):y = 20 # 局部变量 locals# 1)在eval作用域中,没有关于x,y的gloabals或locals,因此调用环境下的gloabals和localsa = eval'x+y')print'a:', a) # a的值为30# 2)在eval作用域中,只存在关于x,y的gloabals,因此调用作用域下的gloabalsb = eval'x+y',{'x':1, 'y': 2})print'b:', b) # b的值为3# 3)在eval作用域中,存在关于x的gloabals,y的locals,因此调用作用域下x的gloabals和y的localsc = eval'x+y',{'x':1, 'y': 2},{'y':3, 'z': 4})print'c:', c) # c的值为4# 4)非计算表达式,返回数据为空d = eval'printx,y)')print'd:', d) # d的值为Nonefun)
六、数据类型
基础数据类型
1、数值 int float bool complex
2、序列 str list tuple
3、散列 set dict
序列、散列既是可迭代对象,也是容器对象。
数据类型扩展
1、命名元组
(1)正常的元组,取值方式为下表取值
tu = "a", "b", "c")tu[0]
(2)使用命名元组取值更方便,而且保留元组特性
2、推导式
(1)列表推导式
如何快速生成一个偶数列表,如data0,data2,data4 … data98
li = ['data{}'.formati) for i in range100) if i % 2 ==0]printli)
(2)字典推导式
特定字符串生成字典,格式:dic = {key:value for i in XXX}
str1 = """ a="1=1=1",b="2",c="3",d="4" """dic = {i.split"=")[0]:i.split"=")[1] for i in str1.split",")}printdic)a = {'x' : 1,'y' : 2,'z' : 3}
b = {'w' : 10,'x' : 11,'y' : 2}printa.keys) - {'z', 'w'}) # {'y', 'x'}c = {key:a[key] for key in a.keys) - {'z', 'w'}}
printc)
3)迭代器推导式
七、python参数详解
参数类别
实参:实际上的参数 形参:形式上的参数
如上图,实际输入的参数为实参,在函数中占位置的参数为形参。
实参类型
1、位置实参
定义:调用的时候,需要根据位置传递实际参数
如图,Lion / Dog占了两个位置,期望是传递实参建议按照这个顺序,不然,即时能够接受,也不能正确的处理。(Lion的名字跟Dog的名字,因为传递错误,将导致不能正确识别)
2、关键字实参
定义:调用的时候,需要指定形参名称
如图,在调用的时候直接赋值,这种传递方式一目了然。
3、默认值实参
定义:在函数中,直接赋值给参数
如图,直接在函数定义一个默认值,在调用的时候,可以不传递,当然也可以传递。
形参类型
形参类型分为非关键字形参*args, 关键字形参**kwargs,先上代码体验一下:
def func*args, **kwargs):print"-------------------------")print'args is {}'.formatargs))print'kwarg is {}'.formatkwargs))try:print'firstArgs is {}'.formatargs[0]))print'firstKwarg is {}'.formatkwargs["a"]))except:passif __name__ == "__main__":func1, 2) # 元组funca=1, b=2, c=3, d=4) # 字典func1, 2, a=1, b=2) # 元组+字典
如上,我们了解了非关键字形参*args, 关键字形参**kwargs的用法。 非关键字形参:用于接收不含关键字的输入数据(如:1,2,3),会以元组的形式进入函数; 非关键字形参:用于接收含有关键字的输入数据(如:key=1中的key),会以字典的形式进入函数;
注意事项:
(1)、*args, kwargs可单独存在
(2)、*args, kwargs同时存在时,函数定义的形参,args形参必须在前
(3)、args, kwargs同时存在时,传递实参的顺序,args实参必须在前
(4)、取值方式: args可通过列表取值方式,如:args[0] *kwargs可通过字典取值方式,如:kwargs[“key”]
(5)、参数扩展:可以跟普通形参一起使用,如:funca,b,c="name",args, kwargs)
八、迭代器、生成器和匿名函数
迭代器
与序列、散列一样,既是可迭代对象,也是容器对象。所不同的是,迭代器比序列、散列多出了next方法。迭代器优点:节约内存循环过程中,数据不用一次读入,在处理文件对象时特别有用,因为文件也是迭代器对象)、不依赖索引取值、实现惰性计算需要时再取值计算);
from pathlib import Pathpths = [pth for pth in Path.cwd).iterdir)]# 如果是os.listdir)这会返回一个list,文件非常多的时候比较耗内存
生成器
这是一种特殊的迭代器,生成器自动实现了“迭代器协议”(即iter和next方法),不需要再手动实现两方法。相对迭代器,生成器在迭代的过程中可以改变当前迭代值。具有yield关键字的函数都是生成器,yield可以理解为return,返回后面的值给调用者。不同的是return返回后,函数会释放,而生成器则不会。在直接调用next方法或用for语句进行下一次迭代时,生成器会从yield下一句开始执行,直至遇到下一个yield。
#!/usr/bin/env python# coding=utf-8# 生成方式一gen = i for i in range10))printnextgen))printnextgen))# 生成方式二def myListnum): # 定义生成器now = 0 # 当前迭代值,初始为0while now < num:val = yield now) # 返回当前迭代值,并接受可能的send发送值;yield在下面会解释now = now + 1 if val is None else val # val为None,迭代值自增1,否则重新设定当前迭代值为valmy_list = myList5) # 得到一个生成器对象print my_list.next) # 返回当前迭代值print my_list.next)my_list.send3) # 重新设定当前的迭代值print my_list.next)print dirmy_list) # 返回该对象所拥有的方法名,可以看到__iter__与next在其中def dedupeitems):seen = set)for item in items:if item not in seen:yield itemseen.additem)a = [1, 5, 2, 1, 9, 1, 5, 10]printlistdedupea)))def dedupeitems, key=None):seen = set)for item in items:val = item if key is None else keyitem)if val not in seen:yield item # 在直接调用next方法或用for语句进行下一次迭代时,生成器会从yield下一句开始执行,直至遇到下一个yieldseen.addval)a = [ {'x':1, 'y':2}, {'x':1, 'y':3}, {'x':1, 'y':2}, {'x':2, 'y':4}]
printlistdedupea, key=lambda d: d['x'],d['y']))))
printlistdedupea, key=lambda d: d['x'])))
匿名函数
这是不需要显式的指定函数名。关键字lambda表示匿名函数,冒号前面的n表示函数参数,可以有多个参数。匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突。此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数:有些函数在代码中只用一次,而且函数体比较简单,使用匿名函数可以减少代码量,看起来比较"优雅“。
#这段代码def calcx,y):return x**y#换成匿名函数calc = lambda x,y:x**yprintcalc2,5))def calcx,y):if x > y:return x*yelse:return x / y#三元运算换成匿名函数calc = lambda x,y:x * y if x > y else x / yprintcalc2,5))import heapq
portfolio = [
{'name': 'IBM', 'shares': 100, 'price': 91.1},
{'name': 'AAPL', 'shares': 50, 'price': 543.22},
{'name': 'FB', 'shares': 200, 'price': 21.09},
{'name': 'HPQ', 'shares': 35, 'price': 31.75},
{'name': 'YHOO', 'shares': 45, 'price': 16.35},
{'name': 'ACME', 'shares': 75, 'price': 115.65}
]
cheap = heapq.nsmallest3, portfolio, key=lambda s: s['price'])
expensive = heapq.nlargest3, portfolio, key=lambda s: s['price'])
printcheap)
printexpensive)
# portfolio这个序列的元素,传入到key的lambda匿名函数中。这些元素被操作后,会按照操作结果进行筛选。
九、高阶函数、递归函数、闭包和偏函数
高阶函数
当一个函数可以作为参数传给另外一个函数,或者一个函数的返回值为另外一个函数(若返回值为该函数本身,则为递归),如果满足其一,则为高阶函数。常见的高阶函数:map)、reduce)、filter)等也是python内置的函数,也可以自定义高阶函数,其实装饰器也算一种高阶函数。通过这些介绍应该能对高阶函数有一个详细的了解。
# 首先来看看函数作为入参的高阶函数:def sonfunc):print"in the sonfunc..")def highfuncfunc):func)print"in the highfunc..")highfuncsonfunc)# 上面的sonfunc函数作为了入参传递给了highfunc函数,highfunc)是一个高阶函数;# 首先来看看函数作为返回的高阶函数:def sonfunc):print"in the sonfunc..")def highfuncfunc):print"in the highfunc..")return funcres=highfuncsonfunc)res)# 上面的sonfunc函数作为了返回的方式,被highfunc函数给返回了,highfunc)是一个高阶函数;# map)使用方法:# map) 会根据提供的函数对指定序列做映射,用法:# mapfunc,iterable...) 第一个参数是提供的函数,第二个参数是指定的序列,序列内的元素是一个或者多个。def squarex):return x**2list1=[1,3,5,7]res=mapsquare,list1)# 由于结果res是一个Iterator对象,Iterator是惰性序列,因此通过list)函数让它把整个序列都计算出来并返回一个list。printlistres))#匿名函数的写法printlistmaplambda x: x ** 2, [1, 2, 3, 4, 5])))# reduce)使用方法:# 该函数将一个数据集合(链表,元组等)中的所有数据进行下列操作:用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果;# reduce)函数接收的的参数和map)类似,一个函数f,一个list,但行为和map)不同,reduce)传入的参数f必须接受2个参数;from functools import reducedef addx,y):return x+yprintreduceadd,[1,2,3,4,5]))# filter)函数使用方法:# filter)函数是python内置的另一个有用的高阶函数,filter)函数接收一个函数f和一个list,这个函数f的作用是对每个元素进行判断,返回true或false,filter)根据判断结果自动过滤掉不符合条件的元素,返回由符合条件的元素组成的list;# 格式:filterfunction, iterable)```pythonlist1=[1,2,3,4,5,6,7,8,9,10]def evenx):return x%2!=1printlistfiltereven,list1)))#匿名函数的写法printlistfilterlambda x:x%2==0,[1,2,3,4,5,6,7,8,9,10])))# sorted) 函数使用方法:# 对所有可迭代的对象进行排序操作,返回的是一个新的 list,而不是在原来的基础上进行的操作。相关参数:reverse -- 排序规则,reverse = True 降序 , reverse = False 升序(默认)。# 利用key进行倒序排序example_list = [5, 0, 6, 1, 2, 7, 3, 4]result_list = sortedexample_list, key=lambda x: x*-1)printresult_list)
递归函数
指的是一个函数在内部调用自身。递归的特性:1、递归函数必须有一个明确的结束条件。2、每进入更深一层的递归时,问题规模相对于上一次递归都应减少。3、相邻两次重复之间有紧密的联系,前一次要为后一次做准备(通常前一次的输出就作为后一次的输入)。4、递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)
import sys#通过循环来实现叠加def sum1n):'''1 to n,The sum function'''sum = 0for i in range1,n + 1):sum += ireturn sum#通过函数的递归来实现叠加def sum2n):'''1 to n,The sum function'''if n > 0:return n + sum_recun - 1) #调用函数自身else:return 0print"循环叠加-->",sum1100))print"递归叠加-->",sum2100))#两者实现的效果均是:5050#通过递归函数实现列表元素相加items0 = [1, 10, 7, 4, 5, 9]
items1 = [1]
def sumitems):head, *tail = itemsreturn head + sumtail) if tail else headprintsumitems0))
printsumitems1))
# 这里有三目运算符(三元运算符)return head + sumtail) if tail else head,即如果tail为真,返回head + sumtail),如果tail为假(没有元素),则直接返回head。# 这是个递归函数、第一次执行return head+sum(tail)此时head =1,tail=[10,7,4,5,9],返回值是1+sum([10,7,4,5,9]),这里的sum(tail)再去调用函数def sum(items),head变成10,tail=[7,4,5,9],此时返回值是1+10+sum([7,4,5,9])一直递归下去,tail =[9],调用sum(tail),head=9,tail=[],因为tail为空,return head 也就是9,此时返回值是return 1+10+7+4+5+9=36
闭包函数
在编程语言中,闭包也称为词法闭包或函数闭包)是在具有一流函数的语言中实现词法范围的名称绑定的一种技术。操作,一个闭包是一个记录存储功能加上一个环境:映射关联每个自由变量的函数在本地变量使用,但是一个封闭范围中定义)的价值或存储位置的名字创建绑定时关闭。与普通函数不同,闭包允许函数通过访问捕获的变量。内部函数包含对外部作用域而非全剧作用域名字的引用,该内部函数称为闭包。函数函数在定义阶段名字的查找顺序就已经固定死了不会因为函数调用位置的变化而改变。 我们来看看要实现函数闭包要满足什么条件(缺一不可): 1.必须嵌套函数。 2.内嵌函数必须引用一个定义在闭合范围内外部函数里)的变量——内部函数引用外部变量 3.外部函数必须返回内嵌函数——必须返回那个内部函数 闭包的作用:可以保持程序上一次运行后的状态然后继续执行。
# 直接传参def indexname):printname)# 闭包传参def foo):num=1def addn):# 若num在函数内,则使用nonlocal;若num在函数外,则使用 globalnonlocal numnum += nreturn numreturn add# 初始化,num=1f=foo)# 传入参数,不再经过num=1,因此,num不断变化printf1)) #2printf2)) #4printf3)) #7printf4)) #11# nonlocal与global的区别:# 第一,两者的功能不同。global关键字修饰变量后标识该变量是全局变量,对该变量进行修改就是修改全局变量,而nonlocal关键字修饰变量后标识该变量是上一级函数中的局部变量,如果上一级函数中不存在该局部变量,nonlocal位置会发生错误(最上层的函数使用nonlocal修饰变量必定会报错)。# 第二,两者使用的范围不同。global关键字可以用在任何地方,包括最上层函数中和嵌套函数中,即使之前未定义该变量,global修饰后也可以直接使用,而nonlocal关键字只能用于嵌套函数中,并且外层函数中定义了相应的局部变量,否则会发生错误(见第一)。
偏函数
是将所要承载的函数作为partial)函数的第一个参数,原函数的各个参数依次作为partial)函数后续的参数,除非使用关键字参数。函数在执行时,要带上所有必要的参数进行调用。但是,有时参数可以在函数被调用之前提前获知。这种情况下,一个函数有一个或多个参数预先就能用上,以便函数能用更少的参数进行调用。简单总结functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。
import functools# 定义一个求余函数def remainderm, n):return m % nprintremainder100, 7)) # 2# 使用偏函数,定义其中一个或多个变量new_rmd = functools.partialremainder, 100)printnew_rmd7)) # 2
十、装饰器
1、开放封闭原则:
封闭:已经实现的功能代码对修改是封闭的
开放:对代码功能的扩展是开放的
2、装饰器是开放封闭原则的一种体现 作用:在不修改功能代码的同时,给代码扩展新的功能
# 装饰器的使用与理解def docaretorfunc):def wrapper):print'add function')# **被装饰的函数有返回值,需要加 return**func)return wrapper@docaretor # @docaretor等价于func = docaretorfunc)def fun):print'test')func)# “装饰带参数的函数”的装饰器def docfunc):def wrappera, b):# 针对要装饰的函数,进行传参print'add function')funca, b)return wrapper@docdef add_numbera, b):print'a+b的结果', a+b)add_number11, 22)# 装饰器通用语法1:装饰带参数的函数# 为了解决不同函数要求的参数传入,需要写一个通用装饰器使用不定传参)def docsfunc):def wrapper*args, **kwargs):# 针对要装饰的函数,进行传参# print'add function')print*args, **kwargs)func*args, **kwargs)return wrapper@docsdef add_numbera, b):print'a+b的结果', a+b)@docsdef add_number01a=1, b=2, c=3):print'a+b的结果', c+d)add_number11, 22)add_number0115, 6, 5)# 装饰器通用语法2:装饰器本身带参数,并装饰带参数的函数多层装饰器)def musenname, age):def docsfunc):def wrapper*args, **kwargs):# 针对要装饰的函数,进行传参# print'add function')printname, age)print*args, **kwargs)func*args, **kwargs)return wrapperreturn docs@musen'ye', 18) # add_number002 = musen'ye', 18)add_number002)def add_number002a, b):print'a+b的结果', a+b)# 通过装饰器装饰类的语法:def class_decoratecls):cls.name = 'musen'cls.age = 19return cls@class_decorateclass MyTest:pass# 通过装饰器装饰类的通用语法:def docs001func):def wrapper*args, **kwargs):# 针对要装饰的函数,进行传参# print'add function')print*args, **kwargs)# **类是有返回值的,因此要加return**return func*args, **kwargs)return wrapper@doc001class MyTestCases:passres = MyTestCasesprintres)# 统计对象个数count = {}def doc002func):count[func] = 0def wrapper*args, **kwargs):count[func] += 1# **类是有返回值的,因此要加return**return func*args, **kwargs)return wrapper@doc002class MyTest001:passMyTest001)MyTest001)printcount)# python中类里面内置的装饰器class MyTest002:@classmethoddef func1cls):print'@classmethod')@staticmethoddef func2):print'@staticmethod')@propertydef func3self):return '@property' # 只读属性# 调用方式MyTest002.func1)MyTest002.func2)MyTest002).func3 # 实例属性# 通过类去实现装饰器# __call__:该方法可以让对象跟函数一样调用class MyClass:def __call__self, *args, **kwargs):print'__call__')# 调用方式m = MyClass)m)class Decorate:def __init__self, func):print'传进来的函数', func)self.func = funcdef __call__self, *args, **kwargs):# 可任意添加装饰器代码self.func*args, **kwargs)@Decoratedef func):print"原功能函数")func)# 递归函数缓存装饰器from functools import lru_cache@lru_cachemaxsize=128)def funcn)if n == 1 or n ==2:return 1else:res = funcn-1) + funcn - 2)return resres = func500)printres)
十一、魔术方法
在Python中,所有以双下划线包起来的方法,都统称为“Magic Method”(魔术方法),例如类的初始化方法。Python对象都是魔术方法来实现的。
# __new__ 方法# 在通过类创建对象的时候调用# 注意点:一般用于实现单例模式的时候重写,如果重写了new方法,一定要调用父类的new方法,因为new方法决定了返回的对象。# 标准单例模式class MytestClassobject):__obj = None@staticmethoddef __new__cls, *args, **kwargs):if not cls.__obj:# 如果没有创建多个对象,就调用父类创建一个cls.__obj = super).__new__cls)return cls.__objelse:# 如果创建了,则把第一次的对象返回return cls.__objm1 = MytestClass)m2 = MytestClass)m3 = MytestClass)printidm1))printidm2))printidm3))# 单例模式装饰器def singletoncls):__instance = {}def wrapper*args, **kwargs):if cls in __instance:return __instance[cls]else:__instance[cls] = cls*args, **kwargs)return __instance[cls]return wrapper@singletonclass A:def __init__self,x=0):self.x = xa1 = A1)a2 = A2)printa1)printa2)# 单例模式装饰器01def single01cls):s=[] //这里定义了一个私有列表,也可以声明一个变量,在wrap用关键字nonlocal去调用def wrap*args,**kwargs):if not s:s.appendcls*args,**kwargs))return sreturn wrap@single01class Aobject):def __init__self,name):self.name = name# 单例模式装饰器002class single02object):def __init__self,cls):self._cls = clsself._instances = None;def __call__self,*args):if not self._instances:self._instances = self._cls*args)return self._instances@single02class Aobject):def __init__self,name):self.name = name# 上下文管理器协议# __enter__ 与 __exit__ 方法# with 可以开启一个对象的上下文管理器协议# 实现逻辑class MyOpen:def __enter__self):print'enter')def __exit__self, exc_type, exc_val, exc_tb):print'exit')obj = MyOpen)with obj as f:print'打开后的文件')#enter#打开后的文件#exit# 上下文管理器的实现class MyOpen01:def __init__self, filename, mode, encoding='utf8'):self.filename = filenameself.mode = modeself.encoding = encodingself.f = openself.filename, self.mode, encoding=self.encoding)def __enter__self):return self.fdef __exit__self, exc_type, exc_val, exc_tb):self.f.close)with MyOpen01'test01.txt', 'w') as f01:f01.write'gigigigigigi')# __str__方法# 该方法用于对象被print打印后,返回的值只能是字符串class MyOpen02:def __init__self, filename, mode, encoding='utf8'):self.filename = filenameself.mode = modeself.encoding = encoding# # 没有该方法,将打印对象本身,而非返回值# def __str__self):# return self.filenamea1 = MyOpen02'a1', 'w')a2 = MyOpen02'a2', 'w')printa1)printa2)### 算数运算符的实现原理# __add__方法class MyOpen03:def __init__self, age):self.age = agedef __str__self):return strself.age)def __add__self, other):'''该方法中定义对象相加的逻辑和返回值'''return self.age + other.ageres1 = MyOpen0318)res2 = MyOpen0312)# 返回的是__str__的值printres1,res2)printtyperes1),typeres2))printtyperes1+res2))# 返回的是 __add__的值printres1+res2)# 同理:# __sub__定义减的行为# __mul__定义乘的行为# __truediv__定义除法的行为# __floordiv__定义整数除法的行为# __mod__定义取余算法的行为# 构造器与析构器# __init__ 与 __new__ 组成完整的构造器# __del__ 形成对象,或对象被删除时自动调用。或程序完成时自动销毁class mydel:def __del__:print'del')m = mydeldel m
十二、伪多态与鸭子类型
# 伪多态: 一个父类具有多个子类,不同子类调用相同的方法,产生不同的执行结果def funcname:int):# python函数没有类型限制printname)func'sdaasdasd')class gradapaobject):def __init__self,money):self.money = moneydef pself):print"this is gradapa")class fathergradapa):def __init__self,money,job):super).__init__money)self.job = jobdef pself):print"this is father,我重写了父类的方法")class mothergradapa):def __init__self, money, job):super).__init__money)self.job = jobdef pself):print"this is mother,我重写了父类的方法")return 100#定义一个函数,函数调用类中的p)方法def fcobj):obj.p)gradapa1 = gradapa3000)father1 = father2000,"工人")mother1 = mother1000,"老师")fcgradapa1) #这里的多态性体现是向同一个函数,传递不同参数后,可以实现不同功能.fcfather1)printfcmother1))===运行结果:===================================================================================this is gradapathis is father,我重写了父类的方法this is mother,我重写了父类的方法100# 鸭子类型:看起来像就行,即具备同样名称的方法,无论类是什么类型,无论方法是什么内容,都可以调用class A:def runself):print'A')class B:def runself):print'B')def func1name:Base):# python没有类型限制,所以没有多态name.run)
十三、数据和自省
类里面的私有属性:声明这个属性仅限类内部使用,外部不要去引用(如果私有属性进行修改,需要通知其他开发人员) _ 开头:在类外面可以直接调用 __ 开头:在类外面可以间接访问
类里面的私有方法:与私有属性一致
dict 属性:以字典的形式显示传入的参数 slots 属性:可以覆盖dict来节省内存 ,可以限制类对象的属性绑定
属性的操作:getattribute、getattr、setattr、delattr,用于自定义属性规则
class Mytest:attr = 100_attr = 200__attr = 300def __init__self, name):self.name = namedef __funcself):print'__func')def _funcself):print'_func')# 类方法的第一个参数必须是类 cls@classmethoddef func1cls):# 方法内部:需要应用类属性或者是类方法,内部的逻辑和类有关,和具体的某一个对象无关cls.attrpass# 静态方法可以直接调用@staticmethoddef func2):# 方法内部封装的代码:不会涉及到类,也不会涉及到具体的对象# 纯粹的功能代码,不受内部影响,不涉及逻辑实现pass# 实例方法,第一个参数是实例对象selfdef func3self):# 实例方法内部:封装和实例对象的相关操作# 方法内部通常 会涉及到实例对象的属性或方法的应用self.namepass# 调用方式:都可以调用,伪私有printMytest.attr)printMytest._attr)printMytest._Mytest__attr)m = Mytest)m._func)m._Mytest__func)# __dict__ 以字典形式传入参数class Mydict:'自定义类:文档注释'name = 'mydict'def __init__self, age):self.age = agedef funcself):print'func')m = Mydict18)printm.__dict__)# {'age': 18}# __slots__ 可以覆盖__dict__来节省内存 ,可以限制类对象的属性绑定class Myslots:'自定义类:文档注释'__slots__ = ['age', 'name']gg = 88def __init__self, age):self.age = agedef funcself):print'func')m = Myslots18)# m.gg = 'gg' # 'Myslots' object attribute 'gg' is read-only# m.ggboy = 'ggboy' # 'Myslots' object has no attribute 'ggboy'm.name = 'name' # 只有__slots__内的值才能被修改m.age = 19 # # 只有__slots__内的值才能被修改printm.age)printm.name)printm.__slots__)# 属性的操作:用于自定义属性规则class MyMagic1:def __getattribute__self, item):print'-----访问属性的方法------')printitem)'''访问属性的时候,会调用这个方法,这个方法的返回值就是查找的属性值'''return super).__getattribute__item)def __getattr__self, item):'''该方法在属性查找不存在的情况下,会报错,用于自动捕获异常'''passdef __setattr__self, key, value):'''属性设置的方法,属性设置时会触发,如__init__传参'''super).__setattr__key, value) # 只有父类object才有设置方法,重写后需要调用def __delattr__self, item):'''删除属性的时候会调用'''passm = MyMagic1)m.name = 'gogo'printm.name)# -----访问属性的方法------# name# gogo#自定义属性访问机制的案例#定义一个User类:#属性:#name:属性值是字符串#name属性不能被删除#skill属性值如果没有被添加,则返回Noneclass User:def __setattr__self, key, value):if self.key=='name':if isinstancevalue,str):super).__setattr__key,value)else:raise AttributeError'name属性只能是str类型')def __delattr__self,item):if item == 'name':raise AttributeError'name属性不能被删除')else:super).__delattr__item)def __getattr__self, item):if item == 'skill':return Noneelse:return super).__getattr__item)u = User)printu.skill)
十四、元类
万物皆对象:函数、模块、包、类
类—》通过元类—》创建对象
什么是元类: type
元类的作用:创建类
python中,所有的类都是type创建出来的,包括object python3中,所有的类都继承于object,type这个类也是继承于object
那么,先有type还是object?
# 如何通过type去创建类res = type1)printres)# 增加参数创建类def func01self):print'类方法')def __init__self, age):self.age = ageclass Base:name = 'base_name'pass# MyClass01是变量名,真正的类名是MyClass,通常写成一样,使用时采用MyClass01MyClass01 = type'MyClass', Base,),{'name':'gg','attr':19,'func':func01, '__init__':__init__})# 自定义创建元类class MyTypetype):def __new__cls, *arg, **kwargs):pass
十五、内存管理机制
Python的内存管理机制可以从三个方面来讲: 1)内存池机制 2)垃圾回收机制
# 同一对象:同一内存块,才是同一对象a = 10b = 10a is b# Ture (小整数池机制)# 深浅复制(copy)li = [1,2,3]li1 = [1,2,3]li2 = lili3 = li.copy)printidli),idli1),idli2),idli3))# li2与li是同一对象,而与li1不是同一对象# li3是独立的对象# 如果变量赋值一个数据,则会在内存分配一片内存存放数据对象# 引用,则直接指向引用的数据# copy,会在内存中重新分配一片内存存放复制对象a = [1,3]b = [4,a]# c = [4,a]c = b.copy) # 浅复制:复制对象的时候,会重新划分一块内存存储的对象,关于对应的数据的引用不会复制。只会复制对象,不会复制对象中引用的数据# d = [4, [1,3]]d = b.deepcopy) # 深复制:复制对象的同时,也会将引用的数据复制# 内存池机制# 小整数池机制:在python中,-5到256之间的数据,缓存这些数据,用于提升程序运行的效率a = -10b = -10a is b# False# 大整数池机制:在运行py程序的时候,解释器会专门分配一块空白的内存,用来存放纯单词字符组成的字符串(数字,字母,下划线)a = 'a_1'b = 'a_1'a is b# Turea = 'a-1'b = 'a-1'a is b# False# 垃圾回收机制# 引用计数机制为主,标记-清除和分代收集两种机制位为辅的策略# 引用计数:对象的引用计数为0,python的垃圾回收机制会将该对象从内存删除,并自动释放# 引用计数增加方式:变量赋值(a=10),变量引用b=a),被其他的对象引用li=[1,2,a])# 引用计数减少方式:变量被删除,变量引用了其他对象,变量离开了所在的作用域(函数调用结束),在其他对象中被移除(li.removea))n1 = 'ABC'n11 = n1n12 = n1 # 'ABC'的引用计数为3n1 = '111' # 'ABC'的引用计数为2del n11 # 'ABC'的引用计数为1del n12 # 'ABC'的引用计数为0,自动释放内存n2 = 'DEF'n3 = 'GHI'# 循环引用-》内存泄漏a = [1,2]b = [3,4]a.appendb)a.appenda)# 即使删除也无法制止del adel b# 标记-清除:先标记对象(垃圾检测),然后清除垃圾# 首先初始所有对象标记为白色,并确定根节点对象(全局变量,这些对象不会被删除),标记他们为黑色(表示对象有效)# 从根节点开始往下找,将根节点引用的对象标记进行标记,然后再继续往下找,把所有有效对象标记为黑色# 所有根节点全部查找完之后,最后剩下的白色节点都是需要清楚的对象# 分代回收:着眼于提升垃圾回收的效率,变量在内存中的创建和销毁,总有频繁和不那么频繁# 先对引用计数为0的对象进行删除,再把标记清除为白色的对象删除。# 第0代链表,对象达到700,开始回收垃圾回收,未被回收就会进入下一代;第0代回收10次后,第1代再进行回收,未被回收进入下一代;第二代重复第一代的过程。# gc模块可以对垃圾回收机制进行设置import gcprintgc,get_threshold))
十六、并发:多线程
多任务:操作系统痛同时运行多个任务 cpu与多任务的关系:单核cpu也可以执行多任务,操作系统轮流让各个任务交替执行,每个任务执行0.01秒后进行切换,为多个程序提供服务。因为执行太快了,感觉是同时执行。这是并发,但不是并行,真正的并行执行只能在多核cpu上实现。 并发:指的是任务数多于cpu核数,通过操作系统的各种任务调度算法,实现用多个任务一起执行,因为速度很快,看上去一起执行一样。 并行:指的是任务数小于cpu核数,即任务真的是一起执行的。 同步:是指线程在访问某一资源的时候,获得了资源的返回结果,才会执行其他操作 异步:是指线程在访问某一资源时,无论是否取得返回结果,都进行下一步操作 python中如何实现多任务:线程、进程、协程
# 多线程模块 threading,其中Thread用于创建线程import timefrom threading import Threaddef work1):for i in range6):time.sleep1)print'{}'.formatstri+1)))def work2):for i in range5):time.sleep1)print'{}'.formatstri+1)))t1 = Threadtarget=work1)t2 = Threadtarget=work2)t1.start)t2.start)# 传参:target指定线程对象,args或kwargs传入参数,name对线程命名def work3name):for i in range6):time.sleep1)print'{},{}'.formatname,stri+1)))def work4name):for i in range5):time.sleep1)print'{},{}'.formatname,stri+1)))t3 = Threadtarget=work3,args='gg',))t4 = Threadtarget=work4,kwargs={'name':'gg',})# 启动线程:异步执行的状态t3.start)t4.start)# 等待主线程执行结束,如果没有这个,执行线程的同时,也会往下执行t3.join)t4.join)# 通过继承类的形式来创建多线程,一个线程指定一个任务,即一个线程类(run方法下面可以放多个线程,但没必要)class MyThreadThread):def __init__self,name):super).__init__)self.name = namedef runself):for i in range6):time.sleep1)print'{}'.formatself.name))m = MyThread'gogoi')m1 = MyThread'gogoi')m.start)m1.start)# 一个问题:多线程可以共享一个全局变量,但存在数据不准确的问题,也就是python无法实现并行,只能实现并发,原因是线程需要“GIL全局解释器锁”获得之后才能执行,即一个时刻只能由一个线程执行。当遇到耗时等待,会自动释放GIL锁;当线程执行时间达到一定的阈值,也会释放GIL锁。如下情况,会出现数据n相互覆盖的情况,即出现了资源竞争,导致全局变量不准确。n = 100def work5name):global nfor i in range500000):n += 1def work6name):global nfor i in range500000):n += 1t1 = Threadtarget=work5)t2 = Threadtarget=work6)t1.start)t2.start)print'n':n)# 解决问题:互斥锁解决资源竞争的问题import threadingm = 100def work7name):global mfor i in range500000):# 获取锁lock.acquire)m += 1# 释放锁lock.release)def work8name):global mfor i in range500000):# 获取锁lock.acquire)m += 1# 释放锁lock.release)# 创建一把锁lock = threading.Lock)# 上锁 lock.acquire)# 等待上锁# 释放锁 lock.release)t7 = Threadtarget=work7)t8 = Threadtarget=work8)t7.start)t8.start)t7.join)t8.join)print'm':m)# 死锁import threadingm1 = 100def work9name):global m1for i in range500000):# 获取锁lockA.acquire)lockB.acquire)m1 += 1# 释放锁lockB.acquire)lockA.release)def work10name):global m1for i in range500000):# 获取锁lockB.acquire)lockA.acquire)m1 += 1# 释放锁lockA.acquire)lockB.release)# 创建两把锁lockA = threading.Lock)lockB = threading.Lock)# 上锁 lock.acquire)# 等待上锁# 释放锁 lock.release)t9 = Threadtarget=work9)t10 = Threadtarget=work10)t9.start)t10.start)t9.join)t10.join)print'm1':m1)