一个函数封装一个功能,你使用的软件可能就是由n多个函数组成的(先备考虑面向对象)。比如抖音这个软件,不可能将所有程序都写入一个文件,所以咱们应该将文件划分,这样其组织结构要好并且代码不冗余。假如分了10个文件,每个文件里面可能都有相同的功能(函数),怎么办?所以将这些相同的功能封装到一个文件中,那么这个存储着很多常用的功能的py文件,就是模块。模块就是文件,存放一堆常用的函数,谁用谁拿。怎么拿?比如:我要策马奔腾共享人世繁华,应该怎么样?我应该骑马,你也要去浪,你是不是也要骑马。我们说一个函数就是一个功能,那么把一些常用的函数放在一个py文件中,这个文件就称之为模块,模块,就是一些列常用功能的集合体。
为什么使用模块:1)从文件级别组织程序,更方便管理随着程序的发展,功能越来越多,为了方便管理,我们通常将程序分成一个个的文件,这样做程序的结构更清晰,方便管理。这时我们不仅仅可以把这些文件当做脚本去执行,还可以把他们当做模块来导入到其他的模块中,实现了功能的重复利;2)拿来主义,提升开发效率同样的原理,我们也可以下载别人写好的模块然后导入到自己的项目中使用,这种拿来主义,可以极大地提升我们的开发效率,避免重复造轮子。
把相似的功能放在一个文件里,要用到的时候,引入就可以直接调用。
模块可以被多次导用吗?
importsysprint(sys.modules)#python内部很多模块在这个里面通过打印能打印出buildinprint(sys.modules.get('my_module'))#验证importmy_module#这个咱们自己写的模块print(sys.modules.get('my_module'))#在第一次使用时,系统会将这个模块导入buildin里之后再多次导入这个模块都无效了,因为它已经有了,所有多次导入只会执行一次importmy_moduleimportmy_module#在导入模块的过程中发生了什么?#1,检测喜爱sys.modules是不是已经有这个模块#2,如果有就不继续导入,如果没有#3,创建一个属于这个模块的命名空间#4,执行这个模块中的代码#5,将模块中的名字存储在这个模块的命名空间中如果my_module文件更换了位置,那么再进行import引用就会找不到,这时候需要将模块所在的位置添加到sys.path中,下面有更详细的讲解
importsysprint(sys.path)path='D:\\'sys.path.append(path)importmy_module人们常说的脚本是什么?
如果你退出python解释器然后重新进入,那么你之前定义的函数或者变量都将丢失,因此我们通常将程序写到文件中以便永久保存下来,需要时就通过pythontest.py方式去执行,此时test.py被称为脚本script。
模块的分类:
Python语言中,模块分为三类。
第一类:内置模块,也叫做标准库。此类模块就是python解释器给你提供的,比如我们之前见过的time模块,os模块。标准库的模块非常多(200多个,每个模块又有很多功能),我们这几天就讲常用的十几种,后面课程中还会陆续的讲到。
第二类:第三方模块,第三方库。一些python大神写的非常好用的模块,必须通过pipinstall指令安装的模块,比如BeautfulSoup,Django,等等。大概有6000多个。
第三类:自定义模块。我们自己在项目中定义的一些模块。
import(导入)的使用:导入模块就是执行这个模块里面的内容
被导入的模块拥有独立的名称空间:每个模块都是一个独立的名称空间,定义在这个模块中的函数,把这个模块的名称空间当做全局名称空间,这样我们在编写自己的模块时,就不用担心我们定义在自己模块中全局变量会在被导入时,与使用者的全局变量冲突。
为模块起别名:import'模块名'as'别名'
优点:1,可以将很长的模块名改成很短,方便使用。2,有利于代码的扩展和优化。
导入多个模块:
importos,sys,json(每个模块名间‘,’隔离)
但推荐一个一个写
importos
importsys.....
导入模块顺序:这样比较规范
1,内置模块
2,扩展模块
3,自定义模块
from...import....使用:需要注意在使用from..import..的时候会将整个py文件导入,也就是说会从py文件从头读到尾,执行整个文件
唯一的区别就是:使用from...import...则是将模块中的名字直接导入到当前的名称空间中,所以在当前名称空间中,直接使用名字就可以了、无需加前缀:模块名.
from...import...的方式有好处也有坏处
好处:使用起来方便了
坏处:容易与当前执行文件中的名字冲突,在全局作用域中无法正常使用:执行文件有与模块同名的变量或者函数名,会有覆盖效果
模块中的变量和全局变量会冲突吗?下面的例子就说明会冲突frommy_moduleimportpriceprint(price)#这里会打印1000price=2000print(price)#这里会打印2000my_module模块内容如下'''print('inmymoudle')price=1000deffunc1():print('infunc1')deffunc2():print('infunc2')'''#如果导入的是price那么my_moudel这个名字还有func1都不能再使用了frommy_moduleimportfunc2#这时候price就没法用了,因为会覆盖也就是用全局的变量,而不是模块里面的price说明原因:
一行导入多个:from模块名import模块内部变量1,模块内部变量2,模块内部变量3........
也支持改名from模块名import模块内部变量as‘别名’
from...import*:fromspamimport*把模块中所有的不是以下划线(_)开头的名字都导入到当前位置
大部分情况下我们的python程序不应该使用这种导入方式,因为*你不知道你导入什么名字,很有可能会覆盖掉你之前已经定义的名字。而且可读性极其的差,在交互式环境中导入时没有问题。
可以使用all来控制*(用来发布新版本),在模块名.py中新增一行
__all__=['变量1','变量2']#这样在另外一个文件中用fromspamimport*就这能导入列表中规定的两个名字frommy_moduleimport*print(price)func1()#这时候只能用pricefunc1()无法使用func2()#如果在my_module文件里上面写入__all__那么只有在它后面列表的数据才能被引用__all__=['price','func1']print('inmymoudle')price=1000deffunc1():print('infunc1')deffunc2():print('infunc2')print(price)如果是下面这用情况:在a模块里引用b模块,在b模块中引入a模块,它并不会进入死循环,而是满足模块只导入一次的原则,在添加到sys.modules之后,就不会再进行添加,结果如下
还有这种方式在两个文件里相互import这时候就会出错,出错原因如下:-----模块之间不可以发生循环引用的问题
编译文件pyc文件的问题?
这个pyc文件本来是空的,是在导入一个模块的时候,会产生一个pyc文件就是一个字节码文件()python翻译成字节码,再由字节码转换成机器码)。
如果是首次导入一个文件,会自动生成这个pyc
pyc文件会加快程序的启动效率,但是不会影响执行效率
如果我的pyc文件修改了,pyc文件能自动感知到
py文件的两种功能:
编写好的一个python文件可以有两种用途:
作用:用来控制.py文件在不同的应用场景下执行不同的逻辑(或者是在模块文件中测试代码)
if__name__=='__main__'(这个很好理解,多应用)
模块的搜索路径:
Python中引用模块是按照一定的规则以及顺序去寻找的,这个查询顺序为:先从内存中已经加载的模块进行寻找找不到再从内置模块中寻找,内置模块如果也没有,最后去sys.path中路径包含的模块中寻找。它只会按照这个顺序从这些指定的地方去寻找,如果最终都没有找到,那么就会报错。
内存中已经加载的模块->内置模块->sys.path路径中包含的模块
模块的查找顺序
需要特别注意的是:我们自定义的模块名不应该与系统内置模块重名。虽然每次都说,但是仍然会有人不停的犯错
包:就是一个文件夹:py文件__init__.py
包是一种通过使用‘.模块名’来组织python模块名称空间的方式。#具体的:包就是一个包含有__init__.py文件的文件夹,所以其实我们创建包的目的就是为了用文件夹将文件/模块组织起来#需要强调的是:1.在python3中,即使包下没有__init__.py文件,import包仍然不会报错,而在python2中,包下一定要有该文件,否则import包报错2.创建包的目的不是为了运行,而是被导入使用,记住,包只是模块的一种形式而已,包的本质就是一种模块********************************************************************
fromimport在导入模块的时候:
可以使用'.'来描述文件之间的层级关系
‘.’的左侧永远必须是一个包
导入至少精确到模块
import不能带点
import在导入模块的时候:
导入直接精确到模块
需要导入一些文件方法如下:
importosos.makedirs('glance/api')os.makedirs('glance/cmd')os.makedirs('glance/db')l=[]l.append(open('glance/__init__.py','w'))l.append(open('glance/api__init__.py','w'))l.append(open('glance/api/policy','w'))l.append(open('glance/api/versions.py','w'))l.append(open('glance/cmd/__init__.py','w'))l.append(open('glance/cmd/manage.py','w'))l.append(open('glance/db/models.py','w'))l.append(open('glance/db/__init__.py','w'))map(lambdaf:f.close(),l)直接导入包意味着什么?---直接执行包下面的__init__文件方法是绝对导入
相对导入:有个优秀是不会随着文件的主次级变化而找不到模块导致报错,例如将glance包放在一个aaa文件下
importglance
glance.api.policy.get()
使用相对导入是不能直接执行这种带有相对关系的文件的,如下面的会报错只能在这个文件外面才能正常使用,如上面就会执行成功
什么时候才用到相对导入:
如果你写一个文件不是为了自己去使用它,而是为了给别人提供服务
**************************************************************************************************
为何使用包:
包的本质就是一个文件夹,那么文件夹唯一的功能就是将文件组织起来随着功能越写越多,我们无法将所以功能都放到一个文件中,于是我们使用模块去组织功能,而随着模块越来越多,我们就需要用文件夹将模块文件组织起来,以此来提高程序的结构性和可维护性
序列化的目的
1,序列化模块序列:有序的排列;序列化,将普通的数据类型转换成字符串/bytes的过程
为什么要用序列化呢?要固态的存储一个数据结构;要在网络上传输一个数据结构。
数据结构?容器类型
比如,你的程序中需要一个字典类型的数据存放你的个人信息:
那么你拿到一个str(dic)有什么用?他是根本转化不成dic的(不能用eval很危险),所以很不方便。那么这时候序列化模块就起到作用了,如果你写入文件中的字符串是一个序列化后的特殊的字符串,那么当你从文件中读取出来,是可以转化回原数据结构的。这个就很牛逼了。
下面说的是json序列化,pickle序列化有所不同。
json序列化除了可以解决写入文件的问题,还可以解决网络传输的问题,比如你将一个list数据结构通过网络传给另个开发者,那么你不可以直接传输,之前我们说过,你要想传输出去必须用bytes类型。但是bytes类型只能与字符串类型互相转化,它不能与其他数据结构直接转化,所以,你只能将list--->字符串--->bytes然后发送,对方收到之后,在decode()解码成原字符串。此时这个字符串不能是我们之前学过的str那种字符串,因为它不能反解,必须要是这个特殊的字符串,他可以反解成list这样开发者之间就可以借助网络互传数据了,不仅仅是开发者之间,你要借助网络爬取数据这些数据多半是这种特殊的字符串,你接受到之后,在反解成你需要的数据类型。
对于这个序列化模块我们做一个小小总结:
序列化模块就是将一个常见的数据结构转化成一个特殊的序列,并且这个特殊的序列还可以反解回去。它的主要用途:文件读写数据,网络传输数据。
Python中这种序列化模块有三种:
json模块:(重点):支持数字,字符串,列表,字典,将他们转化成str字符串就是序列化;str-->dict/list/tuple反序列化
元组是作为列表来序列化的,所以在换回来的过程中也只能转成一个列表
pickle模块:
shelve模块:类似于字典的操作方式去操作特殊的字符串(不讲,可以课下了解)。
当然序列化模块中使用最多的的就是json模块,那么接下来,我们讲一下json与pickle模块。
json模块:
json模块是将满足条件的数据结构转化成特殊的字符串,并且也可以反序列化还原回去。
上面介绍我已经说过了,序列化模块总共只有两种用法,要不就是用于网络传输的中间环节,要不就是文件存储的中间环节,所以json模块总共就有两对四个方法:
用于网络传输:dumps、loads:内存中数据类型<->str
用于文件写读:dump、load:用于文件和内存直接数据类型<->str
importjsondic={'k1':'v1','k2':'v2','k3':'v3'}str_dic=json.dumps(dic)#序列化:将一个字典转换成一个字符串print(type(str_dic),str_dic)#
#json.dumpsdic={'k':'v'}ret=json.dumps(dic)withopen('json_demo','w')asf:f.write(ret)withopen('json_demo')asf:str_dic=f.read()d=json.loads(str_dic)print(d)#json.dumpdic={'k':'v'}withopen('json_demo2','w')asf:json.dump(dic,f)withopen('json_demo2')asf:print(json.load(f))
json序列化存储多个数据到同一个文件中
对于json序列化,存储多个数据到一个文件中是有问题的,默认一个json文件只能存储一个json数据,但是也可以解决,举例说明:
对于json存储多个数据到文件中dic1={'name':'oldboy1'}dic2={'name':'oldboy2'}dic3={'name':'oldboy3'}f=open('序列化',encoding='utf-8',mode='a')json.dump(dic1,f)json.dump(dic2,f)json.dump(dic3,f)f.close()f=open('序列化',encoding='utf-8')ret=json.load(f)ret1=json.load(f)ret2=json.load(f)print(ret)上面会报错解决方式:dic1={'name':'oldboy1'}dic2={'name':'oldboy2'}dic3={'name':'oldboy3'}f=open('序列化',encoding='utf-8',mode='a')str1=json.dumps(dic1)f.write(str1+'\n')str2=json.dumps(dic2)f.write(str2+'\n')str3=json.dumps(dic3)f.write(str3+'\n')f.close()f=open('序列化',encoding='utf-8')forlineinf:print(json.loads(line))json的缺点
#json的问题#{1:'v'}-->str-->{'1',"v"}json的key必须是一个字符串#有限的数据类型:数字字符串元组列表字典#不能连续的dump,因为无法loaddic={'k':'v'}withopen('json_demo2','w')asf:json.dump(dic,f)json.dump(dic,f)withopen('json_demo2','w')asf:print(json.load(f))#{"k":"v"}{"k":"v"}它没法识别一些参数
importjsondata={'username':['李华','二愣子'],'sex':'male','age':16}json_dic2=json.dumps(data,sort_keys=True,indent=2,separators=(',',':'),ensure_ascii=False)print(json_dic2)pickle模块:可以处理所有的数据类型;可以连续向文件中dump或者load
pickle模块是将Python所有的数据结构以及对象等转化成bytes类型,然后还可以反序列化还原回去。
刚才也跟大家提到了pickle模块,pickle模块是只能Python语言识别的序列化模块。如果把序列化模块比喻成全世界公认的一种交流语言,也就是标准的话,json就是像是英语,全世界(python,java,php,C,等等)都遵循这个标准。而pickle就是中文,只有中国人(python)作为第一交流语言。
既然只是Python语言使用,那么它支持Python所有的数据类型包括后面我们要讲的实例化对象等,它能将这些所有的数据结构序列化成特殊的bytes,然后还可以反序列化还原。使用上与json几乎差不多,也是两对四个方法。
用于网络传输:dumps、loads
用于文件写读:dump、load
importpickledic={'k1':'v1','k2':'v2','k3':'v3'}str_dic=pickle.dumps(dic)print(str_dic)#bytes类型dic2=pickle.loads(str_dic)print(dic2)#字典#还可以序列化对象importpickledeffunc():print(666)ret=pickle.dumps(func)print(ret,type(ret))#b'\x80\x03c__main__\nfunc\nq\x00.'
按照星的等级划分,三颗星是需要记住的:
hashlib模块:非常重要的一个模块
字典为什么可以快速查值:集合为什么可以去重:
两个不同的数据很有可能出现两个hash值是相同,那么该怎么办呢?会判断两个值是否相等,如果不能继续存储在内存里。
hash知识复习:
#hash()#算法#把一个数据转换成一个数字的算法#在同一次执行的过程中,对同一个可hash的值进行计算得出的结果是相同的,但每次执行结果不相同print(hash('abc'))#下次再执行就变了print(hash('abc'))print(hash('abc'))#做什么用的:#在数据存储方面提供优化的#为什么对同一个值计算hash值每次运行结果不同?#由于每一次分配的内存地址都是同一定相同#所以多次执行同一代码得到的hash值可能不提供#不可变数据类型可hash#可变数据类型不可hashhashlib模块
此模块有人称为摘要算法,也叫做加密算法,或者是哈希算法,散列算法等等,这么多title不用大家记,那么有同学就问他到底是干啥的?简单来说就是做加密和校验使用,它的工作原理给大家简单描述一下:它通过一个函数,把任意长度的数据按照一定规则转换为一个固定长度的数据串(通常用16进制的字符串表示)。
比如:之前我们在一个文件中存储用户的用户名和密码是这样的形式:
小旋风|123456
有什么问题?你的密码是明文的,如果有人可以窃取到这个文件,那么你的密码就会泄露了。所以,一般我们存储密码时都是以密文存储,比如:
小旋风|e10adc3949ba59abbe56e057f20f883e
那么即使是他窃取到这个文件,他也不会轻易的破解出你的密码,这样就会保证了数据的安全。
hashlib模块就可以完成的就是这个功能。
hashlib的特征以及使用要点:
那么刚才我们也说了,hashlib的主要用途有两个:
密码的加密。
文件一致性校验。
hashlib模块就相当于一个算法的集合,这里面包含着很多的算法,算法越高,转化成的结果越复杂,安全程度越高,相应的效率就会越低
1)密码的加密
dic={'e10adc3949ba59abbe56e057f20f883e':123456}
然后通过你的密文获取对应的密码。
加盐加密
#固定的盐:#通过恶意注册破解密码ret=hashlib.md5('xx教育'.encode('utf-8'))#xx教育就是固定的盐ret.update('a'.encode('utf-8'))print(ret.hexdigest())上面的xx教育就是固定的盐,比如你在一家公司,公司会将你们所有的密码在md5之前增加一个固定的盐,这样提高了密码的安全性。但是如果黑客通过手段窃取到你这个固定的盐之后,也是可以破解出来的。所以,我们还可以加动态的盐。
#动态的盐:#密码加密最高的username='努力学习666'ret=hashlib.md5(username[::2].encode('utf-8'))#针对于每个账户,每个账户的盐都不一样ret.update('a'.encode('utf-8'))print(ret.hexdigest())举例说明:
文件的一致性校验,主要是指用MD5(速度快)
hashlib模块除了可以用于密码加密之外,还有一个常用的功能,那就是文件的一致性校验。
linux讲究:一切皆文件,我们普通的文件,是文件,视频,音频,图片,以及应用程序等都是文件。我们都从网上下载过资源,比如我们刚开学时让大家从网上下载pycharm这个软件,当时你可能没有注意过,其实你下载的时候都是带一个MD5或者shax值的,为什么?我们的网络世界是很不安全的,经常会遇到病毒,木马等,有些你是看不到的可能就植入了你的电脑中,那么他们是怎么来的?都是通过网络传入来的,就是你在网上下载一些资源的时候,趁虚而入,当然大部门被我们的浏览器或者杀毒软件拦截了,但是还有一部分偷偷的进入你的磁盘中了。那么我们自己如何验证我们下载的资源是否有病毒呢?这就需要文件的一致性校验了。在我们下载一个软件时,往往都带有一个MD5或者shax值,当我们下载完成这个应用程序时你要是对比大小根本看不出什么问题,你应该对比他们的md5值,如果两个md5值相同,就证明这个应用程序是安全的,如果你下载的这个文件的MD5值与服务端给你提供的不同,那么就证明你这个应用程序肯定是植入病毒了(文件损坏的几率很低),那么你就应该赶紧删除,不应该安装此应用程序。
我们之前说过,md5计算的就是bytes类型的数据的转换值,同一个bytes数据用同样的加密方式转化成的结果一定相同,如果不同的bytes数据(即使一个数据只是删除了一个空格)那么用同样的加密方式转化成的结果一定是不同的。所以,hashlib也是验证文件一致性的重要工具。
将文件校验写在一个函数中:
deffunc(file):withopen(file,mode='rb')asf1:ret=hashlib.md5()ret.update(f1.read())returnret.hexdigest()print(func('hashlib_file1'))校验文件的一致性,如果文件'wahaha'里面数据变了,如加个空格等,那么md5_obj.hexdigest()会改变,如果把空格取消,md5_obj.hexdigest()就会和之前一直。
withopen('wahaha',encoding='utf-8')asf:str_content=f.read()#print(str_content.encode('utf-8'))md5_obj=hashlib.md5()md5_obj.update(str_content.encode('utf-8'))print(md5_obj.hexdigest())对于大文件如果才能校验,将大文件进行分解,每次转化一部分,这个最后得到的和一次全部读取得到的值相同
importhashlibimportoswithopen('诺基亚后台密码.txt',encoding='gbk')asf:file_size=os.path.getsize('诺基亚后台密码.txt')#得到文件大小md5_obj=hashlib.md5()#这里创建的是对象whilefile_size>0:str_content=f.read(1024)md5_obj.update(str_content.encode('gbk'))file_size-=1024print(md5_obj.hexdigest())#和下面得到的值相同withopen('诺基亚后台密码.txt',encoding='gbk')asf:md5_obj=hashlib.md5()#这里创建的是对象str_content=f.read()md5_obj.update(str_content.encode('gbk'))print(md5_obj.hexdigest())configparser模块:只做简单
该模块适用于配置文件的格式与windowsini文件类似,可以包含一个或多个节(section),每个节可以有多个参数(键=值)
logging模块:非常重要
写日志模块:就是记录一个字符串
一些代码在遇到问题的时候,-写给程序员看的
起到一些排错作用;
需要打印出来-在排错的过程中;
在真正提供服务的时候,不需要。
#记录一些用户的行为--写给用户看的,也可以写给公司看的
#记录一个字符串
logging模块:
格式规范
帮你把日志的紧急情况进行分类
函数式简单配置:
importlogging#越往下,越严重只能打印出warningerrorcritical三个logging.debug('debugmessage')logging.info('infomessage')logging.warning('warningmessage')logging.error('errormessage')logging.critical('criticalmessage')默认情况下Python的logging模块将日志打印到了标准输出中,且只显示了大于等于WARNING级别的日志,这说明默认的日志级别设置为WARNING(日志级别等级CRITICAL>ERROR>WARNING>INFO>DEBUG),默认的日志格式为日志级别:Logger名称:用户输出消息。
灵活配置日志级别,日志格式,输出位置:
importloggingfile_handler=logging.FileHandler(filename='x1.log',mode='a',encoding='utf-8',)logging.basicConfig(format='%(asctime)s-%(name)s-%(levelname)s-%(module)s:%(message)s',datefmt='%Y-%m-%d%H:%M:%S%p',handlers=[file_handler,],level=logging.ERROR)logging.error('你好')日志切割
importtimeimportloggingfromloggingimporthandlerssh=logging.StreamHandler()rh=handlers.RotatingFileHandler('myapp.log',maxBytes=1024,backupCount=5)fh=handlers.TimedRotatingFileHandler(filename='x2.log',when='s',interval=5,encoding='utf-8')logging.basicConfig(format='%(asctime)s-%(name)s-%(levelname)s-%(module)s:%(message)s',datefmt='%Y-%m-%d%H:%M:%S%p',handlers=[fh,sh,rh],level=logging.ERROR)foriinrange(1,100000):time.sleep(1)logging.error('KeyboardInterrupterror%s'%str(i))配置参数:
importlogginglogger=logging.getLogger()#创建一个handler,用于写入日志文件fh=logging.FileHandler('test.log',encoding='utf-8')#再创建一个handler,用于输出到控制台ch=logging.StreamHandler()formatter=logging.Formatter('%(asctime)s-%(name)s-%(levelname)s-%(message)s')fh.setLevel(logging.DEBUG)fh.setFormatter(formatter)ch.setFormatter(formatter)logger.addHandler(fh)#logger对象可以添加多个fh和ch对象logger.addHandler(ch)logger.debug('loggerdebugmessage')logger.info('loggerinfomessage')logger.warning('loggerwarningmessage')logger.error('loggererrormessage')logger.critical('loggercriticalmessage')logging库提供了多个组件:Logger、Handler、Filter、Formatter。Logger对象提供应用程序可直接使用的接口,Handler发送日志到适当的目的地,Filter提供了过滤日志信息的方法,Formatter指定日志显示格式。另外,可以通过:logger.setLevel(logging.Debug)设置级别,当然,也可以通过
fh.setLevel(logging.Debug)单对文件流设置某个级别。
collections模块:
在内置数据类型(dict、list、set、tuple)的基础上,collections模块还提供了几个额外的数据类型:Counter、deque、defaultdict、namedtuple和OrderedDict等。
1.namedtuple:生成可以使用名字来访问元素内容的tuple
2.deque:双端队列,可以快速的从另外一侧追加和推出对象
3.Counter:计数器,主要用来计数
4.OrderedDict:有序字典
5.defaultdict:带有默认值的字典
namedtuple:我们知道tuple可以表示不变集合,例如,一个点的二维坐标就可以表示成:
p=(1,2)但是,看到(1,2),很难看出这个tuple是用来表示一个坐标的。
这时,namedtuple就派上了用场:
>>>fromcollectionsimportnamedtuple>>>Point=namedtuple('Point',['x','y'])>>>p=Point(1,2)>>>p.x>>>p.y类似的,如果要用坐标和半径表示一个圆,也可以用namedtuple定义:
namedtuple('名称',[属性list]):Circle=namedtuple('Circle',['x','y','r'])deque:
使用list存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了,因为list是线性存储,数据量大的时候,插入和删除效率很低。
deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈:
>>>fromcollectionsimportdeque>>>q=deque(['a','b','c'])>>>q.append('x')>>>q.appendleft('y')>>>qdeque(['y','a','b','c','x'])deque除了实现list的append()和pop()外,还支持appendleft()和popleft(),这样就可以非常高效地往头部添加或删除元素。
OrderedDict:
使用dict时,Key是无序的。在对dict做迭代时,我们无法确定Key的顺序。
如果要保持Key的顺序,可以用OrderedDict:(这个方法还得再实验,有待验证)
>>>fromcollectionsimportOrderedDict>>>d=dict([('a',1),('b',2),('c',3)])>>>d#dict的Key是无序的{'a':1,'c':3,'b':2}>>>od=OrderedDict([('a',1),('b',2),('c',3)])>>>od#OrderedDict的Key是有序的OrderedDict([('a',1),('b',2),('c',3)])Counter:
Counter类的目的是用来跟踪值出现的次数。它是一个无序的容器类型,以字典的键值对形式存储,其中元素作为key,其计数作为value。计数值可以是任意的Interger(包括0和负数)。Counter类和其他语言的bags或multisets很相似。
c=Counter('abcdeabcdabcaba')printc输出:Counter({'a':5,'b':4,'c':3,'d':2,'e':1})time模块:
>>>importrandom#随机小数>>>random.random()#大于0且小于1之间的小数0.7664338663654585>>>random.uniform(1,3)#大于1小于3的小数1.6270147180533838#随机整数>>>random.randint(1,5)#大于等于1且小于等于5之间的整数>>>random.randrange(1,10,2)#大于等于1且小于10之间的奇数----顾头不顾尾,可以设置步长#随机选择一个返回>>>random.choice([1,'23',[4,5]])##1或者23或者[4,5]#随机选择多个返回,返回的个数为函数的第二个参数>>>random.sample([1,'23',[4,5]],2)##列表元素任意2个组合[[4,5],'23']#打乱列表顺序>>>item=[1,3,5,7,9]>>>random.shuffle(item)#打乱次序>>>item[5,1,3,7,9]>>>random.shuffle(item)>>>item[5,9,7,1,3]生成随机验证码:
importrandomdefv_code():code=''foriinrange(5):num=random.randint(0,9)alf=chr(random.randint(65,90))add=random.choice([num,alf])code="".join([code,str(add)])returncodeprint(v_code())生成随机验证码re模块
什么是正则?
正则就是用一些具有特殊含义的符号组合到一起(称为正则表达式)来描述字符或者字符串的方法。或者说:正则就是用来描述一类事物的规则。(在Python中)它内嵌在Python中,并通过re模块实现。正则表达式模式被编译成一系列的字节码,然后由用C编写的匹配引擎执行。
它的作用:检测字符串是否符合要求;从大段文字中找到符合要求的内容,作用应用在str
李杰李莲李二
表示重复零次或一次,即只匹配"李"后面一个任意字符李.*李杰和李莲英和李二棍子李杰和李莲英和李二棍子*表示重复零次或多次,即匹配"李"后面0或多个任意字符李.+李杰和李莲英和李二棍子李杰和李莲英和李二棍子+表示重复一次或多次,即只匹配"李"后面1个或多个任意字符李.{1,2}李杰和李莲英和李二棍子李杰和李莲英李二棍
{1,2}匹配1到2次任意字符注意:前面的*,+,等都是贪婪匹配,也就是尽可能匹配,后面加号使其变成惰性匹配,还有{n,m}它也是尽可能匹配m,多的
李杰李莲英李二棍子
表示匹配"李"字后面[杰莲英二棍子]的字符任意次李[^和]*李杰和李莲英和李二棍子李杰李莲英李二棍子
表示匹配一个不是"和"的字符任意次[\d]456bdha34563
表示匹配任意一个数字,匹配到4个结果[\d]+456bdha34563
表示匹配任意个数字,匹配到2个结果
身份证号码是一个长度为15或18个字符的字符串,如果是15位则全部数字组成,首位不能为0;如果是18位,则前17位全部是数字,末位可能是数字或x,下面我们尝试用正则来表示:
110101198001017032
1101011980010170
表示也可以匹配这串数字,但这并不是一个正确的身份证号码,它是一个16位的数字^[1-9]\d{14}(\d{2}[0-9x])$1101011980010170False
现在不会匹配错误的身份证号了()表示分组,将\d{2}[0-9x]分成一组,就可以整体约束他们出现的次数为0-1次^([1-9]\d{16}[0-9x]|[1-9]\d{14})$110105199812067023110105199812067023
表示先匹配[1-9]\d{16}[0-9x]如果没有匹配上就匹配[1-9]\d{14}
在正则表达式中,有很多有特殊意义的是元字符,比如\n和\s等,如果要在正则中匹配正常的"\n"而不是"换行符"就需要对"\"进行转义,变成'\\'。
在python中,无论是正则表达式,还是待匹配的内容,都是以字符串的形式出现的,在字符串中\也有特殊的含义,本身还需要转义。所以如果匹配一次"\n",字符串中要写成'\\n',那么正则里就要写成"\\\\n",这样就太麻烦了。这个时候我们就用到了r'\n'这个概念,此时的正则是r'\\n'就可以了。
因为在正则表达式中\是有特殊意义的字符,所以要匹配\n本身,用表达式\n无法匹配\\n\nTrue转义\之后变成\\,即可匹配"\\\\n"'\\n'True如果在python中,字符串中的'\'也需要转义,所以每一个字符串'\'又需要转义一次r'\\n'r'\n'True在字符串之前加r,让整个字符串不转义
贪婪匹配:在满足匹配时,匹配尽可能长的字符串,默认情况下,采用贪婪匹配(贪婪匹配,回溯算法)