python模块-常用模块

模块是什么:

   一个函数封装一个功能,你使用的软件可能就是由n多个函数组成的(先备考虑面向对象)。比如抖音这个软件,不可能将所有程序都写入一个文件,所以咱们应该将文件划分,这样其组织结构要好并且代码不冗余。假如分了10个文件,每个文件里面可能都有相同的功能(函数),怎么办?所以将这些相同的功能封装到一个文件中,那么这个存储着很多常用的功能的py文件,就是模块。 模块就是文件,存放一堆常用的函数,谁用谁拿。怎么拿?比如:我要策马奔腾共享人世繁华,应该怎么样?我应该骑马,你也要去浪,你是不是也要骑马。 我们说一个函数就是一个功能,那么把一些常用的函数放在一个py文件中,这个文件就称之为模块,模块,就是一些列常用功能的集合体。

 

 

为什么使用模块:1)从文件级别组织程序,更方便管理 随着程序的发展,功能越来越多,为了方便管理,我们通常将程序分成一个个的文件,这样做程序的结构更清晰,方便管理。这时我们不仅仅可以把这些文件当做脚本去执行,还可以把他们当做模块来导入到其他的模块中,实现了功能的重复利;2)拿来主义,提升开发效率 同样的原理,我们也可以下载别人写好的模块然后导入到自己的项目中使用,这种拿来主义,可以极大地提升我们的开发效率,避免重复造轮子。

把相似的功能放在一个文件里,要用到的时候,引入就可以直接调用。

模块可以被多次导用吗?

import sys
print(sys.modules)#python内部很多模块在这个里面  通过打印能打印出build in
print(sys.modules.get('my_module'))#验证
import my_module#这个咱们自己写的模块
print(sys.modules.get('my_module'))#在第一次使用时,系统会将这个模块导入build in里 之后再多次导入这个模块都无效了,因为它已经有了,所有多次导入只会执行一次
import my_module
import my_module
#在导入模块的过程中发生了什么?
#1,检测喜爱sys.modules是不是已经有这个模块
#2,如果有就不继续导入,如果没有
#3,创建一个属于这个模块的命名空间
#4,执行这个模块中的代码
#5,将模块中的名字存储在这个模块的命名空间中

如果my_module文件更换了位置,那么再进行import引用就会找不到,这时候需要将模块所在的位置添加到sys.path中,下面有更详细的讲解

import sys
print(sys.path)
path = 'D:\\'
sys.path.append(path)
import my_module

人们常说的脚本是什么?

如果你退出python解释器然后重新进入,那么你之前定义的函数或者变量都将丢失,因此我们通常将程序写到文件中以便永久保存下来,需要时就通过python test.py方式去执行,此时test.py被称为脚本script。

所以,脚本就是一个python文件,比如你之前写的购物车,模拟博客园登录系统的文件等等

模块的分类:

Python语言中,模块分为三类。

    第一类:内置模块,也叫做标准库。此类模块就是python解释器给你提供的,比如我们之前见过的time模块,os模块。标准库的模块非常多(200多个,每个模块又有很多功能),我们这几天就讲常用的十几种,后面课程中还会陆续的讲到。

    第二类:第三方模块,第三方库。一些python大神写的非常好用的模块,必须通过pip install 指令安装的模块,比如BeautfulSoup, Django,等等。大概有6000多个。

    第三类:自定义模块。我们自己在项目中定义的一些模块。

import(导入)的使用:导入模块就是执行这个模块里面的内容

被导入的模块拥有独立的名称空间:每个模块都是一个独立的名称空间,定义在这个模块中的函数,把这个模块的名称空间当做全局名称空间,这样我们在编写自己的模块时,就不用担心我们定义在自己模块中全局变量会在被导入时,与使用者的全局变量冲突。

为模块起别名:import '模块名' as '别名'

  优点:1,可以将很长的模块名改成很短,方便使用。2,有利于代码的扩展和优化。

导入多个模块:

import os,sys,json  (每个模块名间‘,’隔离)

但推荐一个一个写

import os

import sys  .....

导入模块顺序:这样比较规范

1,内置模块

2,扩展模块

3,自定义模块

from...import....使用:需要注意在使用from..import..的时候会将整个py文件导入,也就是说会从py文件从头读到尾,执行整个文件

唯一的区别就是:使用from...import...则是将模块中的名字直接导入到当前的名称空间中,所以在当前名称空间中,直接使用名字就可以了、无需加前缀:模块名.

from...import...的方式有好处也有坏处

    好处:使用起来方便了

    坏处:容易与当前执行文件中的名字冲突, 在全局作用域中无法正常使用:执行文件有与模块同名的变量或者函数名,会有覆盖效果

模块中的变量和全局变量会冲突吗? 下面的例子就说明 会冲突
from my_module import price
print(price) #这里会打印1000
price= 2000
print(price) #这里会打印2000


my_module模块内容如下
'''
print('in my moudle')

price = 1000
def func1():
    print('in func1')
def func2():
    print('in func2')
'''
#如果导入的是price 那么my_moudel这个名字还有func1都不能再使用了
from my_module import func2
#这时候price就没法用了,因为会覆盖 也就是用全局的变量,而不是模块里面的
price

说明原因:

 

一行导入多个:from 模块名 import 模块内部变量1,模块内部变量2,模块内部变量3........

也支持改名      from 模块名 import 模块内部变量 as  ‘别名’

from ...import *: from spam import * 把模块中所有的不是以下划线(_)开头的名字都导入到当前位置

    大部分情况下我们的python程序不应该使用这种导入方式,因为*你不知道你导入什么名字,很有可能会覆盖掉你之前已经定义的名字。而且可读性极其的差,在交互式环境中导入时没有问题。

可以使用all来控制*(用来发布新版本),在 模块名.py 中新增一行

__all__=['变量1','变量2'] #这样在另外一个文件中用from spam import *就这能导入列表中规定的两个名字
from my_module import *
print(price)
func1()  #这时候只能用price func1() 无法使用func2()

#如果在my_module文件里上面写入__all__那么只有在它后面列表的数据才能被引用
__all__ = ['price','func1']
print('in my moudle')

price = 1000
def func1():
    print('in func1')
def func2():
    print('in func2')
    print(price)

如果是下面这用情况:在a模块里引用b模块,在b模块中引入a模块,它并不会进入死循环,而是满足模块只导入一次的原则,在添加到sys.modules之后 ,就不会再进行添加,结果如下

 

 还有这种方式  在两个文件里相互import 这时候就会出错,出错原因如下:-----模块之间不可以发生循环引用的问题

 

 

编译文件pyc文件的问题?

 这个pyc文件本来是空的,是在导入一个模块的时候,会产生一个pyc文件 就是一个字节码文件 ()python翻译成字节码,再由字节码转换成机器码)。

 如果是首次导入一个文件,会自动生成这个pyc

pyc文件会加快程序的启动效率,但是不会影响执行效率

如果我的pyc文件修改了,pyc文件能自动感知到

py文件的两种功能:

编写好的一个python文件可以有两种用途:

    一:脚本,一个文件就是整个程序,用来被执行(比如你之前写的模拟博客园登录那个作业等)
      二:模块,文件中存放着一堆功能,用来被导入使用

python为我们内置了全局变量__name__,
   当文件被当做脚本执行时:__name__ 等于'__main__'
   当文件被当做模块导入时:__name__等于模块名

作用:用来控制.py文件在不同的应用场景下执行不同的逻辑(或者是在模块文件中测试代码)

if __name__ == '__main__'  (这个很好理解,多应用)

模块的搜索路径:

  Python中引用模块是按照一定的规则以及顺序去寻找的,这个查询顺序为:先从内存中已经加载的模块进行寻找找不到再从内置模块中寻找,内置模块如果也没有,最后去sys.path中路径包含的模块中寻找。它只会按照这个顺序从这些指定的地方去寻找,如果最终都没有找到,那么就会报错。

    内存中已经加载的模块->内置模块->sys.path路径中包含的模块

#模块的查找顺序
1、在第一次导入某个模块时(比如spam),会先检查该模块是否已经被加载到内存中(当前执行文件的名称空间对应的内存),如果有则直接引用
    ps:python解释器在启动时会自动加载一些模块到内存中,可以使用sys.modules查看
2、如果没有,解释器则会查找同名的内建模块
3、如果还没有找到就从sys.path给出的目录列表中依次寻找spam.py文件。


#sys.path的初始化的值来自于:
The directory containing the input script (or the current directory when no file is specified).
PYTHONPATH (a list of directory names, with the same syntax as the shell variable PATH).
The installation-dependent default.

#需要特别注意的是:我们自定义的模块名不应该与系统内置模块重名。虽然每次都说,但是仍然会有人不停的犯错。 

#在初始化后,python程序可以修改sys.path,路径放到前面的优先于标准库被加载。
>>> import sys
>>> sys.path.append('/a/b/c/d')
>>> sys.path.insert(0,'/x/y/z') #排在前的目录,优先被搜索
注意:搜索时按照sys.path中从左到右的顺序查找,位于前的优先被查找,sys.path中还可能包含.zip归档文件和.egg文件,python会把.zip归档文件当成一个目录去处理,

#首先制作归档文件:zip module.zip foo.py bar.py 
import sys
sys.path.append('module.zip')
import foo,bar

#也可以使用zip中目录结构的具体位置
sys.path.append('module.zip/lib/python')


#windows下的路径不加r开头,会语法错误
sys.path.insert(0,r'C:\Users\Administrator\PycharmProjects\a')
 

#至于.egg文件是由setuptools创建的包,这是按照第三方python库和扩展时使用的一种常见格式,.egg文件实际上只是添加了额外元数据(如版本号,依赖项等)的.zip文件。

#需要强调的一点是:只能从.zip文件中导入.py,.pyc等文件。使用C编写的共享库和扩展块无法直接从.zip文件中加载(此时setuptools等打包系统有时能提供一种规避方法),且从.zip中加载文件不会创建.pyc或者.pyo文件,因此一定要事先创建他们,来避免加载模块是性能下降。

 

模块的查找顺序

  1. 在第一次导入某个模块时(模块名),会先检查该模块是否已经被加载到内存中(当前执行文件的名称空间对应的内存),如果有则直接引用(ps:python解释器在启动时会自动加载一些模块到内存中,可以使用sys.modules查看)

  2. 如果没有,解释器则会查找同名的内置模块

  3. 如果还没有找到就从sys.path给出的目录列表中依次寻找t模块名.py文件。

需要特别注意的是:我们自定义的模块名不应该与系统内置模块重名。虽然每次都说,但是仍然会有人不停的犯错

包:就是一个文件夹:py文件__init__.py

包是一种通过使用‘.模块名’来组织python模块名称空间的方式。

#具体的:包就是一个包含有__init__.py文件的文件夹,所以其实我们创建包的目的就是为了用文件夹将文件/模块组织起来

#需要强调的是:
  1. 在python3中,即使包下没有__init__.py文件,import 包仍然不会报错,而在python2中,包下一定要有该文件,否则import 包报错

  2. 创建包的目的不是为了运行,而是被导入使用,记住,包只是模块的一种形式而已,包的本质就是一种模块

********************************************************************

from  import在导入模块的时候:

  可以使用'.'来描述文件之间的层级关系

  ‘.’的左侧永远必须是一个包

  导入至少精确到模块

  import 不能带点

import 在导入模块的时候:

  可以使用'.'来描述文件之间的层级关系

  ‘.’的左侧永远必须是一个包

  导入直接精确到模块

需要导入一些文件方法如下:

import os
os.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(lambda f:f.close(),l)

直接导入包意味着什么?---直接执行包下面的__init__文件      方法是绝对导入

在init文件上做文章:

相对导入:有个优秀是不会随着文件的主次级变化而找不到模块 导致报错,例如将glance包放在一个aaa文件下

import glance

glance.api.policy.get()

    

使用相对导入 是不能直接执行这种带有相对关系的文件的,如下面的会报错 只能在这个文件外面才能正常使用,如上面就会执行成功

 

 什么时候才用到相对导入:

如果你写一个文件不是为了自己去使用它,而是为了给别人提供服务

**************************************************************************************************

为何使用包:

包的本质就是一个文件夹,那么文件夹唯一的功能就是将文件组织起来
随着功能越写越多,我们无法将所以功能都放到一个文件中,于是我们使用模块去组织功能,而随着模块越来越多,我们就需要用文件夹将模块文件组织起来,以此来提高程序的结构性和可维护性

 

#1.关于包相关的导入语句也分为import和from ... import ...两种,但是无论哪种,无论在什么位置,在导入时都必须遵循一个原则:
凡是在导入时带点的,点的左边都必须是一个包,否则非法。可以带有一连串的点,如item.subitem.subsubitem,但都必须遵循这个原则。
但对于导入后,在使用时就没有这种限制了,点的左边可以是包,模块,函数,类(它们都可以用点的方式调用自己的属性)。
#2、import导入文件时,产生名称空间中的名字来源于文件,import 包,产生的名称空间的名字同样来源于文件,即包下的__init__.py,导入包本质就是在导入该文件 #3、包A和包B下有同名模块也不会冲突,如A.a与B.a来自俩个命名空间

内置模块:

 

序列化的目的

 

1、以某种存储形式使自定义对象持久化

 

2、将对象从一个地方传递到另一个地方。

 

3、使程序更具维护性。

 

 

1,序列化模块 序列:有序的排列;序列化,将普通的数据类型转换成字符串/bytes的过程

为什么要用序列化呢?要固态的存储一个数据结构;要在网络上传输一个数据结构。

数据结构?容器类型

比如,你的程序中需要一个字典类型的数据存放你的个人信息:

 dic = {'username':'太白', 'password': 123,'login_status': True}

  你的程序中有一些地方都需要使用这个dic数据,登录时会用到,注册时也会用到。那么我们之前就是将这个dic写在全局里,但是这样是不合理的,应该是将这数据写入一个地方存储(还没有学数据库)先存放在一个文件中,那么程序中哪里需要这个数据了,你就读取文件取出你需要的信息即可。那么有没有什么问题? 你将这个字典直接写入文件是不可以的,必须转化成字符串的形式,而且你读取出来也是字符串形式的字典。

那么你拿到一个str(dic)有什么用?他是根本转化不成dic的(不能用eval很危险),所以很不方便。那么这时候序列化模块就起到作用了,如果你写入文件中的字符串是一个序列化后的特殊的字符串,那么当你从文件中读取出来,是可以转化回原数据结构的。这个就很牛逼了。

下面说的是json序列化,pickle序列化有所不同。

json序列化除了可以解决写入文件的问题,还可以解决网络传输的问题,比如你将一个list数据结构通过网络传给另个开发者,那么你不可以直接传输,之前我们说过,你要想传输出去必须用bytes类型。但是bytes类型只能与字符串类型互相转化,它不能与其他数据结构直接转化,所以,你只能将list ---> 字符串 ---> bytes 然后发送,对方收到之后,在decode() 解码成原字符串。此时这个字符串不能是我们之前学过的str那种字符串,因为它不能反解,必须要是这个特殊的字符串,他可以反解成list 这样开发者之间就可以借助网络互传数据了,不仅仅是开发者之间,你要借助网络爬取数据这些数据多半是这种特殊的字符串,你接受到之后,在反解成你需要的数据类型。

对于这个序列化模块我们做一个小小总结:

序列化模块就是将一个常见的数据结构转化成一个特殊的序列,并且这个特殊的序列还可以反解回去。它的主要用途:文件读写数据,网络传输数据。

Python中这种序列化模块有三种:

    json模块 : (重点):支持数字,字符串,列表,字典,将他们转化成str字符串就是序列化;str-->dict/list/tuple 反序列化

  1. 不同语言都遵循的一种数据转化格式,即不同语言都使用的特殊字符串。(比如Python的一个列表[1, 2, 3]利用json转化成特殊的字符串,然后在编码成bytes发送给php的开发者,php的开发者就可以解码成特殊的字符串,然后在反解成原数组(列表): [1, 2, 3])

  2. json序列化只支持部分Python数据结构:dict,list, tuple,str,int, float,True,False,None,-->str   ;集合是不能序列化的

            元组是作为列表来序列化的,所以在换回来的过程中也只能转成一个列表

    pickle模块:

  1. 只能是Python语言遵循的一种数据转化格式,只能在python语言中使用。

  2. 支持Python所有的数据类型包括实例化对象。

     

    shelve模块:类似于字典的操作方式去操作特殊的字符串(不讲,可以课下了解)。

当然序列化模块中使用最多的的就是json模块,那么接下来,我们讲一下json与pickle模块。

json模块:

json模块是将满足条件的数据结构转化成特殊的字符串,并且也可以反序列化还原回去。

上面介绍我已经说过了,序列化模块总共只有两种用法,要不就是用于网络传输的中间环节,要不就是文件存储的中间环节,所以json模块总共就有两对四个方法:

    用于网络传输:dumps、loads:内存中 数据类型<->str

    用于文件写读:dump、load :用于文件和内存直接 数据类型<->str

import json
dic = {'k1':'v1','k2':'v2','k3':'v3'}
str_dic = json.dumps(dic)  #序列化:将一个字典转换成一个字符串
print(type(str_dic),str_dic)  #<class 'str'> {"k3": "v3", "k1": "v1", "k2": "v2"}
#注意,json转换完的字符串类型的字典中的字符串是由""表示的
​
dic2 = json.loads(str_dic)  #反序列化:将一个字符串格式的字典转换成一个字典
#注意,要用json的loads功能处理的字符串类型的字典中的字符串必须由""表示
import json
f = open('json_file.json','w')
dic = {'k1':'v1','k2':'v2','k3':'v3'}
json.dump(dic,f)  #dump方法接收一个文件句柄,直接将字典转换成json字符串写入文件
f.close()
# json文件也是文件,就是专门存储json字符串的文件。
f = open('json_file.json')
dic2 = json.load(f)  #load方法接收一个文件句柄,直接将文件中的json字符串转换成数据结构返回
f.close()
print(type(dic2),dic2)

用法的不同

#json.dumps
dic = {'k':'v'}
ret = json.dumps(dic)
with open('json_demo','w') as f:
    f.write(ret)
with open('json_demo')as f:
    str_dic = f.read()
d = json.loads(str_dic)
print(d)
#json.dump
dic = {'k':'v'}
with open('json_demo2','w') as f:
    json.dump(dic,f)

with open('json_demo2') as f:
    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')
for line in f:
    print(json.loads(line))

json的缺点

#json的问题
#{1:'v'}-->str-->{'1',"v"} json的key必须是一个字符串
#有限的数据类型:数字 字符串 元组 列表 字典
#不能连续的dump,因为无法load
dic = {'k':'v'}
with open('json_demo2','w') as f:
    json.dump(dic,f)
    json.dump(dic,f)
with open('json_demo2','w') as f:
    print(json.load(f))  #{"k": "v"}{"k": "v"} 它没法识别

一些参数

Serialize obj to a JSON formatted str.(字符串表示的json对象) 
Skipkeys:默认值是False,如果dict的keys内的数据不是python的基本类型(str,unicode,int,long,float,bool,None),设置为False时,就会报TypeError的错误。此时设置成True,则会跳过这类key 
ensure_ascii:,当它为True的时候,所有非ASCII码字符显示为\uXXXX序列,只需在dump时将ensure_ascii设置为False即可,此时存入json的中文即可正常显示。) 
If check_circular is false, then the circular reference check for container types will be skipped and a circular reference will result in an OverflowError (or worse). 
If allow_nan is false, then it will be a ValueError to serialize out of range float values (nan, inf, -inf) in strict compliance of the JSON specification, instead of using the JavaScript equivalents (NaN, Infinity, -Infinity). 
indent:应该是一个非负的整型,如果是0就是顶格分行显示,如果为空就是一行最紧凑显示,否则会换行且按照indent的数值显示前面的空白分行显示,这样打印出来的json数据也叫pretty-printed json 
separators:分隔符,实际上是(item_separator, dict_separator)的一个元组,默认的就是(‘,’,’:’);这表示dictionary内keys之间用“,”隔开,而KEY和value之间用“:”隔开。 
default(obj) is a function that should return a serializable version of obj or raise TypeError. The default simply raises TypeError. 
sort_keys:将数据根据keys的值进行排序。 
To use a custom JSONEncoder subclass (e.g. one that overrides the .default() method to serialize additional types), specify it with the cls kwarg; otherwise JSONEncoder is used.

其他参数说明

举例说明

import json
data = {'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

import pickle
dic = {'k1':'v1','k2':'v2','k3':'v3'}
str_dic = pickle.dumps(dic)
print(str_dic)  # bytes类型
​
dic2 = pickle.loads(str_dic)
print(dic2)    #字典
# 还可以序列化对象
import pickle
​
def func():
    print(666)
    
ret = pickle.dumps(func)
print(ret,type(ret))  # b'\x80\x03c__main__\nfunc\nq\x00.' <class 'bytes'>
​
f1 = pickle.loads(ret)  # f1得到 func函数的内存地址
f1()  # 执行func函数

#文件操作 dic = {(1,2):'oldboy',1:True,'set':{1,2,3}} f = open('pick序列化',mode='wb') pickle.dump(dic,f) f.close() with open('pick序列化',mode='wb') as f1: pickle.dump(dic,f1)

pickle序列化存储多个数据到一个文件中

dic1 = {'name':'oldboy1'}
dic2 = {'name':'oldboy2'}
dic3 = {'name':'oldboy3'}
​
f = open('pick多数据',mode='wb')
pickle.dump(dic1,f)
pickle.dump(dic2,f)
pickle.dump(dic3,f)
f.close()
​
f = open('pick多数据',mode='rb')
while True:
    try:
        print(pickle.load(f))
    except EOFError:
        break
f.close()

os模块:os模块是与操作系统交互的一个接口,它提供的功能多与工作目录,路径,文件等相关。

按照星的等级划分,三颗星是需要记住的:

当前执行这个python文件的工作目录相关的工作路径
os.getcwd() 获取当前执行目录,即当前python脚本执行的目录路径  ** 
os.chdir("dirname")  改变当前脚本执行目录;相当于shell下cd  **
os.curdir  返回当前目录: ('.')  **
os.pardir  获取当前目录的父目录字符串名:('..') **# 和文件夹相关 
os.makedirs('dirname1/dirname2')    可生成多层递归目录  ***#非常重要
os.removedirs('dirname1') 若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推 ***
os.mkdir('dirname')    生成单级目录,在当前目录下;相当于shell中mkdir dirname ***非常重要
os.rmdir('dirname')    删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname ***非常重要
# os.listdir('dirname')    列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印 **非常重要
# 和文件相关
os.remove()  删除一个文件  ***
os.rename("oldname","newname")  重命名文件/目录  ***
os.stat('path/filename')  获取文件/目录信息 **# 和操作系统差异相关
# os.sep    输出操作系统特定的路径分隔符,win下为"\\",Linux下为"/" *
# os.linesep    输出当前平台使用的行终止符,win下为"\t\n",Linux下为"\n" *
# os.pathsep    输出用于分割文件路径的字符串 win下为;,Linux下为: *
# os.name    输出字符串指示当前使用平台。win->'nt'; Linux->'posix' *#可以用来判断操作系统
# 和执行系统命令相关:模拟在cmd中执行
# os.system("bash command")  运行shell命令,直接显示,可能会乱码  **
# os.popen("bash command).read()  运行shell命令,获取执行结果 ,不会乱码 **非常重要
os.environ  获取系统环境变量  **#path系列,和路径相关#都非常重要
os.path.abspath(path) 返回path规范化的绝对路径  ***
os.path.split(path) 将path分割成目录和文件名二元组返回 ***
os.path.dirname(path) 返回path的目录。其实就是os.path.split(path)的第一个元素  **
os.path.basename(path) 返回path最后的文件名。如何path以/或\结尾,那么就会返回空值,即os.path.split(path)的第二个元素。 **非
os.path.exists(path)  如果path存在,返回True;如果path不存在,返回False  ***
os.path.isabs(path)  如果path是绝对路径,返回True  **
os.path.isfile(path)  如果path是一个存在的文件,返回True。否则返回False  ***
os.path.isdir(path)  如果path是一个存在的文件夹,则返回True。否则返回False  ***
os.path.join(path1[, path2[, ...]])  将多个路径组合后返回,第一个绝对路径之前的参数将被忽略 *** #可以不用顾虑wind还是linux平台,直接可以用
os.path.getatime(path)  返回path所指向的文件或者目录的最后访问时间  **
os.path.getmtime(path)  返回path所指向的文件或者目录的最后修改时间  **
os.path.getsize(path) 返回path的大小 *** #计算文件大小是准确的,但是计算文件夹的大小计算的是这个文件所在硬盘上的大小,是不准确的

注意:os.stat('path/filename') 获取文件/目录信息 的结构说明

stat 结构:
st_mode: inode 保护模式
st_ino: inode 节点号。
st_dev: inode 驻留的设备。
st_nlink: inode 的链接数。
st_uid: 所有者的用户ID。
st_gid: 所有者的组ID。
st_size: 普通文件以字节为单位的大小;包含等待某些特殊文件的数据。
st_atime: 上次访问的时间。
st_mtime: 最后一次修改的时间。
st_ctime: 由操作系统报告的"ctime"。在某些系统上(如Unix)是最新的元数据更改的时间,在其它系统上(如Windows)是创建时间(详细信息参见平台的文档)。

sys模块:

sys.argv           命令行参数List,第一个元素是程序本身路径,可以加一些
sys.exit(n)        退出程序,正常退出时exit(0),错误退出sys.exit(1)
sys.version        获取Python解释程序的版本信息
sys.path           返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值  ***
sys.platform       返回操作系统平台名称 不能精准判断出是多少位

sys.argv作用,在执行这个文件时,可以进行验证 登录操作,用于运维

import sys
print(sys.argv)
if len(sys.argv)==3 and sys.argv[1]=='alex' and  sys.argv[2] ='alex3714':
    print('计算器')
else:
    print('没有通过验证')
    exit()
print(sys.argv)

 

 

 

 

 

 

 

hashlib模块:非常重要的一个模块

 

字典为什么可以快速查值:                                         集合为什么可以去重:

                      

 

 

 两个不同的数据很有可能出现两个hash值是相同,那么该怎么办呢?会判断两个值是否相等,如果不能继续存储在内存里。

 

 

hash知识复习:

#hash()
#算法
    #把一个数据转换成一个数字的算法
    #在同一次执行的过程中,对同一个可hash的值进行计算得出的结果是相同的,但每次执行结果不相同
print(hash('abc'))#下次再执行就变了
print(hash('abc'))
print(hash('abc'))
#做什么用的:
    #在数据存储方面提供优化的
#为什么对同一个值计算hash值每次运行结果不同?
    #由于每一次分配的内存地址都是同一定相同
    #所以多次执行同一代码得到的hash值可能不提供
#不可变数据类型可hash
#可变数据类型不可hash

hashlib模块

#hashlib的特点
#提供多种算法
#同一个字符串用同一种算法进行加密,结果总是相同的,不论在那台电脑上
#同一个字符串用不同的算法进行加密,结果总是不同的
#那为什么要使用hashlib进行加密呢?
#1,登录的时候
            
#            alex3714-->转换-->转换后的结果存储在文件中
    #登录的时候
#            输入密码-->用同样的方法转换-->结果于文件匹配
import hashlib #只和bytes数据打交道,它的hash值是定长 32位的

#md5算法 最常用的
#sha算法 是个系列,随着后面的数字越大,计算的时间越长,,结果越长,越安全
#字符串-->加密的结果 是不可逆的

#md5算法---暴力破解,又叫撞库
md5_obj = hashlib.md5()
md5_obj.update(b'alex3714')
print(md5_obj.hexdigest())

 

此模块有人称为摘要算法,也叫做加密算法,或者是哈希算法,散列算法等等,这么多title不用大家记,那么有同学就问他到底是干啥的? 简单来说就是做加密和校验使用,它的工作原理给大家简单描述一下:它通过一个函数,把任意长度的数据按照一定规则转换为一个固定长度的数据串(通常用16进制的字符串表示)。

比如:之前我们在一个文件中存储用户的用户名和密码是这样的形式:

    小旋风|123456

有什么问题?你的密码是明文的,如果有人可以窃取到这个文件,那么你的密码就会泄露了。所以,一般我们存储密码时都是以密文存储,比如:

    小旋风|e10adc3949ba59abbe56e057f20f883e

那么即使是他窃取到这个文件,他也不会轻易的破解出你的密码,这样就会保证了数据的安全。

hashlib模块就可以完成的就是这个功能。

hashlib的特征以及使用要点:

  1. bytes类型数据 ---> 通过hashlib算法 ---> 固定长度的字符串

  2. 不同的bytes类型数据转化成的结果一定不同。

  3. 相同的bytes类型数据转化成的结果一定相同。

  4. 此转化过程不可逆。

那么刚才我们也说了,hashlib的主要用途有两个:

    密码的加密。

    文件一致性校验。

hashlib模块就相当于一个算法的集合,这里面包含着很多的算法,算法越高,转化成的结果越复杂,安全程度越高,相应的效率就会越低

1)密码的加密

普通加密:我们以常见的摘要算法MD5为例,计算出一个字符串的MD5值
import hashlib
​
md5 = hashlib.md5()
md5.update('123456'.encode('utf-8'))
print(md5.hexdigest())
​
# 计算结果如下:
'e10adc3949ba59abbe56e057f20f883e'# 验证:相同的bytes数据转化的结果一定相同
import hashlib
​
md5 = hashlib.md5()
md5.update('123456'.encode('utf-8'))
print(md5.hexdigest())
​
# 计算结果如下:
'e10adc3949ba59abbe56e057f20f883e'# 验证:不相同的bytes数据转化的结果一定不相同
import hashlib
​
md5 = hashlib.md5()
md5.update('12345'.encode('utf-8'))
print(md5.hexdigest())
​
# 计算结果如下:
'827ccb0eea8a706c4c34a16891f84e7b'

上面就是普通的md5加密,非常简单,几行代码就可以了,但是这种加密级别是最低的,相对来说不很安全。虽然说hashlib加密是不可逆的加密方式,但也是可以破解的,那么他是如何做的呢?你看网上好多MD5解密软件,他们就是用最low的方式,空间换时间。他们会把常用的一些密码比如:123456,111111,以及他们的md5的值做成对应关系,类似于字典,

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())

举例说明:

username =  input('username:')
password = input('password:')
md5_obj = hashlib.md5(''.encode('utf-8'))#加盐
md5_obj.update(password.encode('utf-8'))
md5_str = md5_obj.hexdigest()
with open('useinfo') as f:
    for line in f:
        name,pwd = line.split('|')
        if name==username and pwd == md5_str:
            print('登录成功')
            break

不过一般我们用到MD5加密就可以了。

文件的一致性校验,主要是指用MD5(速度快)

hashlib模块除了可以用于密码加密之外,还有一个常用的功能,那就是文件的一致性校验。

    linux讲究:一切皆文件,我们普通的文件,是文件,视频,音频,图片,以及应用程序等都是文件。我们都从网上下载过资源,比如我们刚开学时让大家从网上下载pycharm这个软件,当时你可能没有注意过,其实你下载的时候都是带一个MD5或者shax值的,为什么? 我们的网络世界是很不安全的,经常会遇到病毒,木马等,有些你是看不到的可能就植入了你的电脑中,那么他们是怎么来的? 都是通过网络传入来的,就是你在网上下载一些资源的时候,趁虚而入,当然大部门被我们的浏览器或者杀毒软件拦截了,但是还有一部分偷偷的进入你的磁盘中了。那么我们自己如何验证我们下载的资源是否有病毒呢?这就需要文件的一致性校验了。在我们下载一个软件时,往往都带有一个MD5或者shax值,当我们下载完成这个应用程序时你要是对比大小根本看不出什么问题,你应该对比他们的md5值,如果两个md5值相同,就证明这个应用程序是安全的,如果你下载的这个文件的MD5值与服务端给你提供的不同,那么就证明你这个应用程序肯定是植入病毒了(文件损坏的几率很低),那么你就应该赶紧删除,不应该安装此应用程序。

我们之前说过,md5计算的就是bytes类型的数据的转换值,同一个bytes数据用同样的加密方式转化成的结果一定相同,如果不同的bytes数据(即使一个数据只是删除了一个空格)那么用同样的加密方式转化成的结果一定是不同的。所以,hashlib也是验证文件一致性的重要工具。

将文件校验写在一个函数中 :

def func(file):
    with open(file,mode='rb') as f1:
        ret = hashlib.md5()
        ret.update(f1.read())
        return ret.hexdigest()
​
​
print(func('hashlib_file1'))

校验文件的一致性,如果文件'wahaha'里面数据变了,如加个空格等,那么md5_obj.hexdigest()会改变,如果把空格取消,md5_obj.hexdigest()就会和之前一直。

with open('wahaha',encoding='utf-8')as f:
    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())

对于大文件如果才能校验,将大文件进行分解,每次转化一部分,这个最后得到的和一次全部读取得到的值相同

import hashlib
import os
with open('诺基亚后台密码.txt',encoding='gbk')as f:
    file_size = os.path.getsize('诺基亚后台密码.txt')#得到文件大小
    md5_obj = hashlib.md5()#这里创建的是对象
    while file_size>0:
        str_content = f.read(1024)
        md5_obj.update(str_content.encode('gbk'))
        file_size -= 1024
    print(md5_obj.hexdigest())#和下面得到的值相同


with open('诺基亚后台密码.txt',encoding='gbk')as f:
    md5_obj = hashlib.md5()#这里创建的是对象
    str_content = f.read()
    md5_obj.update(str_content.encode('gbk'))
    print(md5_obj.hexdigest())

configparser模块:只做简单

该模块适用于配置文件的格式与windows ini文件类似,可以包含一个或多个节(section),每个节可以有多个参数(键=值)

logging模块:非常重要

写日志模块:就是记录一个字符串

一些代码在遇到问题的时候,-写给程序员看的

        起到一些排错作用;

        需要打印出来-在排错的过程中;

        在真正提供服务的时候,不需要。

#记录一些用户的行为--写给用户看的,也可以写给公司看的

#记录一个字符串

logging模块:

    格式规范

    帮你把日志的紧急情况进行分类

函数式简单配置:

import logging  #越往下,越严重 只能打印出warning error critical三个
logging.debug('debug message')  
logging.info('info message')  
logging.warning('warning message')  
logging.error('error message')  
logging.critical('critical message')

默认情况下Python的logging模块将日志打印到了标准输出中,且只显示了大于等于WARNING级别的日志,这说明默认的日志级别设置为WARNING(日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG),默认的日志格式为日志级别:Logger名称:用户输出消息。

#如果想调整日志信息显示情况,需要配置
#基础配置basiConfig
logging.basicConfig(level=logging.DEBUG,#文件从哪个位置读取,从debug开始读取
filename='abc',#将读取的日志存在这个文件中
format='%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s') #可以参考下面的

logging.debug('debug message')   #全部打印出来
logging.info('info message')  
logging.warning('warning message')  
logging.error('error message')  
logging.critical('critical message')
#使用logger对象的形式进行普配置
import logging #通过logger对象俩解决中文乱码问题
logger = logging.getLogger()
fh = logging.FileHandler('log',encoding='utf-8')#创建了一个文件操作符
fmt = logging.Formatter('%(asctime)s -%(filename)s- %(name)s - %(levelname)s - %(message)s')#希望输出的格式
fh.setFormatter(fmt) #相当于fh吸收了fmt,两者产生关系 给文件操作符设置一个格式
logger.addHandler(fh) #给logger对象添加一个fh,两个关联起来
#在屏幕上输入
sh = logging.StreamHandler()#创建了一个屏幕操作符
sh.setFormatter(fmt)
logger.addHandler(sh)

logger.setLevel(logging.DEBUG)
logger.debug('debug message')   
logger.info('info message')  
logger.warning('warning message')  
logger.error('error message')  
logger.critical('critical message')

 

 

灵活配置日志级别,日志格式,输出位置:

import logging

file_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('你好')

日志切割

import time
import logging
from logging import handlers

sh = 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
)

for i in range(1,100000):
    time.sleep(1)
    logging.error('KeyboardInterrupt error %s'%str(i))

配置参数:

logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为,可用参数有:

filename:用指定的文件名创建FiledHandler,这样日志会被存储在指定的文件中。
filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。
format:指定handler使用的日志显示格式。
datefmt:指定日期时间格式。
level:设置rootlogger(后边会讲解具体概念)的日志级别
stream:用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件(f=open(‘test.log’,’w’)),默认为sys.stderr。若同时列出了filename和stream两个参数,则stream参数会被忽略。

format参数中可能用到的格式化串:
%(name)s Logger的名字
%(levelno)s 数字形式的日志级别
%(levelname)s 文本形式的日志级别
%(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
%(filename)s 调用日志输出函数的模块的文件名
%(module)s 调用日志输出函数的模块名
%(funcName)s 调用日志输出函数的函数名
%(lineno)d 调用日志输出函数的语句所在的代码行
%(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示
%(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数
%(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
%(thread)d 线程ID。可能没有
%(threadName)s 线程名。可能没有
%(process)d 进程ID。可能没有
%(message)s用户输出的消息

logger对象配置

便于理解

import logging

logger = 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('logger debug message') 
logger.info('logger info message') 
logger.warning('logger warning message') 
logger.error('logger error message') 
logger.critical('logger critical message')

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就派上了用场:

>>> from collections import namedtuple
>>> 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是为了高效实现插入和删除操作的双向列表,适合用于队列和栈:

>>> from collections import deque
>>> q = deque(['a', 'b', 'c'])
>>> q.append('x')
>>> q.appendleft('y')
>>> q
deque(['y', 'a', 'b', 'c', 'x'])

deque除了实现list的append()和pop()外,还支持appendleft()和popleft(),这样就可以非常高效地往头部添加或删除元素。

OrderedDict:

使用dict时,Key是无序的。在对dict做迭代时,我们无法确定Key的顺序。

如果要保持Key的顺序,可以用OrderedDict:(这个方法还得再实验,有待验证)

>>> from collections import OrderedDict
>>> 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')
print c
输出:Counter({'a': 5, 'b': 4, 'c': 3, 'd': 2, 'e': 1})

time模块:

 和时间有关系的我们就要用到时间模块。在使用模块之前,应该首先导入这个模块。

#常用方法
1.time.sleep(secs)
(线程)推迟指定的时间运行。单位为秒。
2.time.time()
获取当前时间戳

表示时间的三种方式

在Python中,通常有这三种方式来表示时间:时间戳、元组(struct_time)、格式化的时间字符串:

(1)时间戳(timestamp) -(机器语言):通常来说,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量。我们运行“type(time.time())”,返回的是float类型。

(2)格式化的时间字符串(Format String): ‘1999-12-06’----给别人看的

%y 两位数的年份表示(00-99%Y 四位数的年份表示(000-9999%m 月份(01-12%d 月内中的一天(0-31%H 24小时制小时数(0-23%I 12小时制小时数(01-12%M 分钟数(00=59%S 秒(00-59%a 本地简化星期名称
%A 本地完整星期名称
%b 本地简化的月份名称
%B 本地完整的月份名称
%c 本地相应的日期表示和时间表示
%j 年内的一天(001-366%p 本地A.M.或P.M.的等价符
%U 一年中的星期数(00-53)星期天为星期的开始
%w 星期(0-6),星期天为星期的开始
%W 一年中的星期数(00-53)星期一为星期的开始
%x 本地相应的日期表示
%X 本地相应的时间表示
%Z 当前时区的名称
%% %号本身

python中时间日期格式化符号:

python中时间日期格式化符号

(3)元组(struct_time) :struct_time元组共有9个元素共九个元素:(年,月,日,时,分,秒,一年中第几周,一年中第几天等

索引(Index)属性(Attribute)值(Values)
0 tm_year(年) 比如2011
1 tm_mon(月) 1 - 12
2 tm_mday(日) 1 - 31
3 tm_hour(时) 0 - 23
4 tm_min(分) 0 - 59
5 tm_sec(秒) 0 - 60
6 tm_wday(weekday) 0 - 6(0表示周一)
7 tm_yday(一年中的第几天) 1 - 366
8 tm_isdst(是否是夏令时) 默认为0

首先,我们先导入time模块,来认识一下python中表示时间的几种格式:
#导入时间模块
>>>import time

#时间戳
>>>time.time()
1500875844.800804

#时间字符串
>>>time.strftime("%Y-%m-%d %X")
'2017-07-24 13:54:37'
>>>time.strftime("%Y-%m-%d %H-%M-%S")
'2017-07-24 13-55-04'

#时间元组:localtime将一个时间戳转换为当前时区的struct_time   
time.localtime()
#得出结果为'''time.struct_time(tm_year=2020, tm_mon=2, tm_mday=21,
          tm_hour=16, tm_min=19, tm_sec=37, 
                 tm_wday=0, tm_yday=205, tm_isdst=0)'''

小结:时间戳是计算机能够识别的时间;时间字符串是人能够看懂的时间;元组则是用来操作时间的,也就是将时间戳转化成格式化时间

几种格式之间的转换  :字符串时间<->结构化时间<->时间戳时间

 

复制代码
#语法讲解:time.strptime(string[, format])
参数:
string -- 时间字符串;format -- 格式化字符串。

#time.strftime(format[, t])
  • format -- 格式字符串。
  • t -- 可选的参数t是一个struct_time对象。
import time

t = (2009, 2, 17, 17, 3, 38, 1, 48, 0)
t = time.mktime(t)
print time.strftime("%b %d %Y %H:%M:%S", time.gmtime(t))

#import time
#ret = time.strptime('2028-5-21','%Y-%m-%d') #格式化转结构化
#print(ret)

# 格式化时间 ---->  结构化时间
ft = time.strftime('%Y/%m/%d %H:%M:%S')
st = time.strptime(ft,'%Y/%m/%d %H:%M:%S')
print(st)
# 结构化时间 ---> 时间戳
t = time.mktime(st)
print(t)

# 时间戳 ----> 结构化时间
t = time.time()
st = time.localtime(t)
print(st)
# 结构化时间 ---> 格式化时间
ft = time.strftime('%Y/%m/%d %H:%M:%S',st)
print(ft)
 
复制代码

复制代码
#time.asctime(结构化时间) 如果不传参数,直接返回当前时间的格式化串
>>>time.asctime(time.localtime(1500000000))
'Fri Jul 14 10:40:00 2017'
>>>time.asctime()
'Mon Jul 24 15:18:33 2017'

#时间戳 --> %a %d %d %H:%M:%S %Y串
#time.ctime(时间戳)  如果不传参数,直接返回当前时间的格式化串
>>>time.ctime()
'Mon Jul 24 15:19:07 2017'
>>>time.ctime(1500000000)
'Fri Jul 14 10:40:00 2017' 

t = time.time()
ft = time.ctime(t)
print(ft)

st = time.localtime()
ft = time.asctime(st)
print(ft)
复制代码
#如何计算时间差:
#2018-5-22 11:02:50---时间戳1
#2018-5-21 12:59:30---时间戳2
#float = 时间戳1-时间龊
#float-->结构化时间
#结构化时间 - 1970 1 1 0 0 0 ---得到两个时间的差

 


datetime模块


# datatime模块
import datetime
now_time = datetime.datetime.now()  # 现在的时间
# 只能调整的字段:weeks days hours minutes seconds
print(datetime.datetime.now() + datetime.timedelta(weeks=3)) # 三周后
print(datetime.datetime.now() + datetime.timedelta(weeks=-3)) # 三周前
print(datetime.datetime.now() + datetime.timedelta(days=-3)) # 三天前
print(datetime.datetime.now() + datetime.timedelta(days=3)) # 三天后
print(datetime.datetime.now() + datetime.timedelta(hours=5)) # 5小时后
print(datetime.datetime.now() + datetime.timedelta(hours=-5)) # 5小时前
print(datetime.datetime.now() + datetime.timedelta(minutes=-15)) # 15分钟前
print(datetime.datetime.now() + datetime.timedelta(minutes=15)) # 15分钟后
print(datetime.datetime.now() + datetime.timedelta(seconds=-70)) # 70秒前
print(datetime.datetime.now() + datetime.timedelta(seconds=70)) # 70秒后

current_time = datetime.datetime.now()
# 可直接调整到指定的 年 月 日 时 分 秒 等

print(current_time.replace(year=1977))  # 直接调整到1977年
print(current_time.replace(month=1))  # 直接调整到1月份
print(current_time.replace(year=1989,month=4,day=25))  # 1989-04-25 18:49:05.898601

# 将时间戳转化成时间
print(datetime.date.fromtimestamp(1232132131))  # 2009-01-17

random模块

>>> import random
#随机小数
>>> 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]

生成随机验证码:

import random

def v_code():

    code = ''
    for i in range(5):

        num=random.randint(0,9)
        alf=chr(random.randint(65,90))
        add=random.choice([num,alf])
        code="".join([code,str(add)])

    return code

print(v_code())

生成随机验证码

re模块

什么是正则?

  正则就是用一些具有特殊含义的符号组合到一起(称为正则表达式)来描述字符或者字符串的方法。或者说:正则就是用来描述一类事物的规则。(在Python中)它内嵌在Python中,并通过 re 模块实现。正则表达式模式被编译成一系列的字节码,然后由用 C 编写的匹配引擎执行。

它的作用:检测字符串是否符合要求;从大段文字中找到符合要求的内容,作用应用在str

元字符
 
匹配内容
\w 匹配字母(包含中文)或数字或下划线
\W 匹配非字母(包含中文)或数字或下划线
\s 匹配任意的空白符
\S 匹配任意非空白符
\d 匹配数字
\D 匹配非数字
\A 从字符串开头匹配
\z 匹配字符串的结束,如果是换行,只匹配到换行前的结果
\n 匹配一个换行符
\t 匹配一个制表符
^ 匹配字符串的开始(永远出现在开头)
$ 匹配字符串的结尾(它仅是约束,不代表字符字符)
. 匹配任意字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符。
[...] 匹配字符组中的字符
[^...] 匹配除了字符组中的字符的所有字符
* 匹配0个或者多个左边的字符。----量词
+ 匹配1个或者多个左边的字符。----量词
匹配0个或者1个左边的字符,非贪婪方式。----量词
{n} 重复左边字符n次。---量词   {n,}重复n次或更多次
{n,m} 匹配n到m次由前面的正则表达式定义的片段,贪婪方式---量词
a|b 匹配a或者b。(例如ab|abc匹配abc,只会匹配到‘ab’,因为它遇到ab就会停,不再匹配,所以可以调过来abc|ab)-也就是左右有重叠的,要把长的放在前边
() 匹配括号内的表达式,也表示一个组,里面只能使用|操作符

. ^ $

正则 待匹配字符 匹配
结果
说明
海. 海燕海娇海东 海燕海娇海东   匹配所有"海."的字符
^海. 海燕海娇海东 海燕 只从开头匹配"海."
  海.$   海燕海娇海东 海东 只匹配结尾的"海.$"

 

* + ? { }

正则 待匹配字符 匹配
结果
说明
李.? 李杰和李莲英和李二棍子

李杰
李莲
李二

 
?表示重复零次或一次,即只匹配"李"后面一个任意字符
 
李.* 李杰和李莲英和李二棍子 李杰和李莲英和李二棍子
*表示重复零次或多次,即匹配"李"后面0或多个任意字符
李.+ 李杰和李莲英和李二棍子 李杰和李莲英和李二棍子
+表示重复一次或多次,即只匹配"李"后面1个或多个任意字符
李.{1,2} 李杰和李莲英和李二棍子

李杰和
李莲英
李二棍

{1,2}匹配1到2次任意字符

 注意:前面的*,+,?等都是贪婪匹配,也就是尽可能匹配,后面加?号使其变成惰性匹配,还有{n,m}它也是尽可能匹配m,多的

正则 待匹配字符 匹配
结果
说明
李.*? 李杰和李莲英和李二棍子

惰性匹配

 

字符集[][^]

正则 待匹配字符 匹配
结果
说明
李[杰莲英二棍子]* 李杰和李莲英和李二棍子

李杰
李莲英
李二棍子

 
表示匹配"李"字后面[杰莲英二棍子]的字符任意次
 
李[^和]* 李杰和李莲英和李二棍子

李杰
李莲英
李二棍子

表示匹配一个不是"和"的字符任意次
[\d] 456bdha3

4
5
6
3

表示匹配任意一个数字,匹配到4个结果
[\d]+ 456bdha3

456
3

表示匹配任意个数字,匹配到2个结果

 

分组 ()与 或 |[^]

 身份证号码是一个长度为15或18个字符的字符串,如果是15位则全部🈶️数字组成,首位不能为0;如果是18位,则前17位全部是数字,末位可能是数字或x,下面我们尝试用正则来表示:

正则 待匹配字符 匹配
结果
说明
^[1-9]\d{13,16}[0-9x]$ 110101198001017032

110101198001017032

   表示可以匹配一个正确的身份证号
^[1-9]\d{13,16}[0-9x]$ 1101011980010170

1101011980010170

表示也可以匹配这串数字,但这并不是一个正确的身份证号码,它是一个16位的数字
^[1-9]\d{14}(\d{2}[0-9x])?$ 1101011980010170

False

现在不会匹配错误的身份证号了
()表示分组,将\d{2}[0-9x]分成一组,就可以整体约束他们出现的次数为0-1次
^([1-9]\d{16}[0-9x]|[1-9]\d{14})$ 110105199812067023

110105199812067023

表示先匹配[1-9]\d{16}[0-9x]如果没有匹配上就匹配[1-9]\d{14}

 

转义符 \

在正则表达式中,有很多有特殊意义的是元字符,比如\n和\s等,如果要在正则中匹配正常的"\n"而不是"换行符"就需要对"\"进行转义,变成'\\'。

在python中,无论是正则表达式,还是待匹配的内容,都是以字符串的形式出现的,在字符串中\也有特殊的含义,本身还需要转义。所以如果匹配一次"\n",字符串中要写成'\\n',那么正则里就要写成"\\\\n",这样就太麻烦了。这个时候我们就用到了r'\n'这个概念,此时的正则是r'\\n'就可以了。

正则 待匹配字符 匹配
结果
说明
\n \n  False
因为在正则表达式中\是有特殊意义的字符,所以要匹配\n本身,用表达式\n无法匹配
\\n \n  True
转义\之后变成\\,即可匹配
"\\\\n" '\\n'  True
如果在python中,字符串中的'\'也需要转义,所以每一个字符串'\'又需要转义一次
r'\\n' r'\n'  True
在字符串之前加r,让整个字符串不转义

 

贪婪匹配

贪婪匹配:在满足匹配时,匹配尽可能长的字符串,默认情况下,采用贪婪匹配(贪婪匹配,回溯算法

就是尽可能的去往后匹配,匹配到后面后,发现没有“>”,又开始往回找

 

 

 

正则 待匹配字符 匹配
结果
说明
<.*>

<script>...<script>

<script>...<script>
默认为贪婪匹配模式,会匹配尽量长的字符串
<.*?> r'\d'  

<script>
<script>

加上?为将贪婪匹配模式转为非贪婪匹配模式,会匹配尽量短的字符串

 

几个常用的非贪婪匹配Pattern
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复
.*?的用法
. 是任意字符
* 是取 0 至 无限长度
? 是非贪婪模式。
何在一起就是 取尽量少的任意字符,一般不会这么单独写,他大多用在:
.*?x

就是取前面任意长度的字符,直到一个x出现

 re模块:三种最基本的用法 findall, search, match

findall函数里面有三个函数(pattern(正则表达式),string(待匹配的字符串),flags=0)

常用方法

import re

ret = re.findall('a', 'eva egon yuan')  # 返回所有满足匹配条件的结果,放在列表里
print(ret) #结果 : ['a', 'a']

ret=re.serach('a','eva egon yuan')
print(ret) #结果:<_sre.SRE_Match object;span.....> 不会直接返回结果,返回的是第一个匹配的变量 ret = re.search('a', 'eva egon yuan').group() #只能通过加上.group() 来返回结果,且只返回第一个结果 print(ret) #结果 : 'a' # 函数会在字符串内查找模式匹配,只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象可以 # 通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None,这时候如果执行group()会报错
所以在执行group()之前先要判断下if ret: ret.group()
ret = re.match('\d+','eva123yuan4')
print(ret)#结果:None
ret = re.match('\d+','123evayuan4')--------相当于ret = re.match('^\d+','123evayuan4'),match强行在正则前面加了个‘^’,只能从头开始匹配
print(ret)#结果:<_sre.SRE_Match object....>
print(ret.group())#结果:123 -----------说明match只能从头匹配,如果第一个匹配不上就返回None,和search一样,只会匹配一个 ret
= re.match('a', 'abc').group() # 同search,不过尽在字符串开始处进行匹配 print(ret) #结果 : 'a' ret = re.split('[ab]', 'abcd') # 先按'a'分割得到''和'bcd',在对''和'bcd'分别按'b'分割--是这样解释的,按‘a’分割,a左边是空所以分割成‘’和‘bcd’
print(ret) # ['', '', 'cd']
ret = re.split('\d+','alex27egon18')   ----在通过正则表达式 切分更具有优越性,功能更多了


ret = re.sub('\d', 'H', 'eva3egon4yuan4', 1)#将数字替换成'H',参数1表示只替换1个 print(ret) #evaHegon4yuan4 ret = re.subn('\d', 'H', 'eva3egon4yuan4')#将数字替换成'H',返回元组(替换的结果,替换了多少次) print(ret) obj = re.compile('\d{3}') #将正则表达式编译成为一个 正则表达式对象,规则要匹配的是3个数字---python并不能理解正则表达式,所以先编译转化成它理解的,再执行 ret = obj.search('abc123eeee') #正则表达式对象调用search,参数为待匹配的字符串 ---------什么时候使用compile,只有在多次用同一个规则正则表达式时才使用 print(ret.group()) #结果 : 123 ------compile就是节省时间 import re ret = re.finditer('\d', 'ds3sy4784a') #finditer返回一个存放匹配结果的迭代器----节省内存 print(ret) # <callable_iterator object at 0x10195f940> ------它和search match一样都需要.group()来查找值 print(next(ret).group()) #查看第一个结果 print(next(ret).group()) #查看第二个结果 print([i.group() for i in ret]) #查看剩余的左右结果

1 findall的优先级查询:

import re

ret = re.findall('www\.(baidu|oldboy)\.com', 'www.oldboy.com')    ---注意需要转义的地方
print(ret)  # ['oldboy']     这是因为findall会匹配好之后,优先返回组里的内容,如果想要匹配结果,取消权限即可---在()里加上(?:)即可取消分组优先

ret = re.findall('www\.(?:baidu|oldboy)\.com', 'www.oldboy.com')
print(ret)  # ['www.oldboy.com']

2 split的优先级查询:

ret=re.split("\d+","eva3egon4yuan")
print(ret) #结果 : ['eva', 'egon', 'yuan']

ret=re.split("(\d+)","eva3egon4yuan")
print(ret) #结果 : ['eva', '3', 'egon', '4', 'yuan']

#在匹配部分加上()之后所切出的结果是不同的,
#没有()的没有保留所匹配的项,但是有()的却能够保留了匹配的项,
#这个在某些需要保留匹配部分的使用过程是非常重要的。

综合练习与扩展

1、匹配标签

import re
ret = re.search('<(\w+)>(\w+)</(\w+)>','<h1>hello</h1>')
print(ret.group()) #结果:<h1>hello</h1>  默认的是0
print(ret.group(0))#结果:<h1>hello</h1> 和上面结果一样
print(ret.group(1))#结果:h1
print(ret.group(2))#结果:hello
print(ret.group(3))#结果:/h1
#最后一个必须和第一组的内容一致,这时候可以用下面的方式’\‘加上组的所在位置即‘1’
ret = re.search(r'<(\w+)>(\w+)</\1>','<h1>hello</h1>')
print(ret.group())
#这样最后一个匹配的就和第一个一样了---(除了最后一个里面有’/‘ 这个要注意点 看清楚)
import re
#给组起名字 可以在正则表达式中引用 如最后一样 和上面一个意思,第一个和最后一个组里面的匹配的内容必须一致
ret = re.search('<(?P<tag>\w+)>(\w+)</(?P=tag)>','<h1>hello</h1>')
print(ret)
print(ret.group())
print(ret.group('tag')) #可以用名字取值

 深一步解释

import re

ret = re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>")
#还可以在分组中利用?P<name>的形式给分组起名字
#获取的匹配结果可以直接用group('名字')拿到对应的值
print(ret.group('tag_name'))  #结果 :h1
print(ret.group())  #结果 :<h1>hello</h1>

ret = re.search(r"<(\w+)>\w+</\1>","<h1>hello</h1>")
#如果不给组起名字,也可以用\序号来找到对应的组,表示要找的内容和前面的组内容一致
#获取的匹配结果可以直接用group(序号)拿到对应的值
print(ret.group(1))
print(ret.group())  #结果 :<h1>hello</h1>

匹配整数

import re #只匹配出下面字符串中的数字? 这个说明有的干扰项也必须的匹配出来才能得到自己想要的数据

ret=re.findall(r"\d+","1-2*(60+(-40.35/5)-(-4*3))")
print(ret) #['1', '2', '60', '40', '35', '5', '4', '3']
ret=re.findall(r"-?\d+\.\d*|(-?\d+)","1-2*(60+(-40.35/5)-(-4*3))")#可以利用‘|’把小数也匹配出来 ,还有可以用findall优先级,只匹配()里面的,这时候
print(ret) #['1', '-2', '60', '', '5', '-4', '3']            #因为小数匹配不上变成了空字符  然后再删除空字符 就可以得到数字了
ret.remove("")
print(ret) #['1', '-2', '60', '5', '-4', '3']

数字匹配

1、 匹配一段文本中的每行的邮箱
      http://blog.csdn.net/make164492212/article/details/51656638

2、 匹配一段文本中的每行的时间字符串,比如:‘1990-07-12’;

   分别取出1年的12个月(^(0?[1-9]|1[0-2])$)、
   一个月的31天:^((0?[1-9])|((1|2)[0-9])|30|31)$

3、 匹配qq号。(腾讯QQ号从10000开始)  [1,9][0,9]{4,}

4、 匹配一个浮点数。       ^(-?\d+)(\.\d+)?$   或者  -?\d+\.?\d*

5、 匹配汉字。             ^[\u4e00-\u9fa5]{0,}$ 

6、 匹配出所有整数

**************************************************************************************************************

常用方法举例# ----------------匹配模式--------------------

# 1,之前学过的字符串的常用操作:一对一匹配
# s1 = 'fdskahf太白金星'
# print(s1.find('太白'))  # 7

# 2,正则匹配:

# 单个字符匹配
import re
# \w 与 \W
# print(re.findall('\w', '太白jx 12*() _'))  # ['太', '白', 'j', 'x', '1', '2', '_']
# print(re.findall('\W', '太白jx 12*() _'))  # [' ', '*', '(', ')', ' ']


# \s 与\S
# print(re.findall('\s','太白barry*(_ \t \n'))  # [' ', '\t', ' ', '\n']
# print(re.findall('\S','太白barry*(_ \t \n'))  # ['太', '白', 'b', 'a', 'r', 'r', 'y', '*', '(', '_']


# \d 与 \D
# print(re.findall('\d','1234567890 alex *(_'))  # ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']
# print(re.findall('\D','1234567890 alex *(_'))  # [' ', 'a', 'l', 'e', 'x', ' ', '*', '(', '_']

# \A 与 ^
# print(re.findall('\Ahel','hello 太白金星 -_- 666'))  # ['hel']
# print(re.findall('^hel','hello 太白金星 -_- 666'))  # ['hel']


# \Z、\z 与 $  @@
# print(re.findall('666\Z','hello 太白金星 *-_-* \n666'))  # ['666']
# print(re.findall('666\z','hello 太白金星 *-_-* \n666'))  # []
# print(re.findall('666$','hello 太白金星 *-_-* \n666'))  # ['666']

# \n 与 \t
# print(re.findall('\n','hello \n 太白金星 \t*-_-*\t \n666'))  # ['\n', '\n']
# print(re.findall('\t','hello \n 太白金星 \t*-_-*\t \n666'))  # ['\t', '\t']


# 重复匹配

# . ? * + {m,n} .* .*?

# . 匹配任意字符,除了换行符(re.DOTALL 这个参数可以匹配\n)。
# print(re.findall('a.b', 'ab aab a*b a2b a牛b a\nb'))  # ['aab', 'a*b', 'a2b', 'a牛b']
# print(re.findall('a.b', 'ab aab a*b a2b a牛b a\nb',re.DOTALL))  # ['aab', 'a*b', 'a2b', 'a牛b']


# ?匹配0个或者1个由左边字符定义的片段。
# print(re.findall('a?b', 'ab aab abb aaaab a牛b aba**b'))  # ['ab', 'ab', 'ab', 'b', 'ab', 'b', 'ab', 'b']


# * 匹配0个或者多个左边字符表达式。 满足贪婪匹配 @@
# print(re.findall('a*b', 'ab aab aaab abbb'))  # ['ab', 'aab', 'aaab', 'ab', 'b', 'b']
# print(re.findall('ab*', 'ab aab aaab abbbbb'))  # ['ab', 'a', 'ab', 'a', 'a', 'ab', 'abbbbb']


# + 匹配1个或者多个左边字符表达式。 满足贪婪匹配  @@
# print(re.findall('a+b', 'ab aab aaab abbb'))  # ['ab', 'aab', 'aaab', 'ab']


# {m,n}  匹配m个至n个左边字符表达式。 满足贪婪匹配  @@
# print(re.findall('a{2,4}b', 'ab aab aaab aaaaabb'))  # ['aab', 'aaab']


# .* 贪婪匹配 从头到尾.
# print(re.findall('a.*b', 'ab aab a*()b'))  # ['ab aab a*()b']


# .*? 此时的?不是对左边的字符进行0次或者1次的匹配,
# 而只是针对.*这种贪婪匹配的模式进行一种限定:告知他要遵从非贪婪匹配 推荐使用!
# print(re.findall('a.*?b', 'ab a1b a*()b, aaaaaab'))  # ['ab', 'a1b', 'a*()b']


# []: 括号中可以放任意一个字符,一个中括号代表一个字符
# - 在[]中表示范围,如果想要匹配上- 那么这个-符号不能放在中间.
# ^ 在[]中表示取反的意思.
# print(re.findall('a.b', 'a1b a3b aeb a*b arb a_b'))  # ['a1b', 'a3b', 'a4b', 'a*b', 'arb', 'a_b']
# print(re.findall('a[abc]b', 'aab abb acb adb afb a_b'))  # ['aab', 'abb', 'acb']
# print(re.findall('a[0-9]b', 'a1b a3b aeb a*b arb a_b'))  # ['a1b', 'a3b']
# print(re.findall('a[a-z]b', 'a1b a3b aeb a*b arb a_b'))  # ['aeb', 'arb']
# print(re.findall('a[a-zA-Z]b', 'aAb aWb aeb a*b arb a_b'))  # ['aAb', 'aWb', 'aeb', 'arb']
# print(re.findall('a[0-9][0-9]b', 'a11b a12b a34b a*b arb a_b'))  # ['a11b', 'a12b', 'a34b']
# print(re.findall('a[*-+]b','a-b a*b a+b a/b a6b'))  # ['a*b', 'a+b']
# - 在[]中表示范围,如果想要匹配上- 那么这个-符号不能放在中间.
# print(re.findall('a[-*+]b','a-b a*b a+b a/b a6b'))  # ['a-b', 'a*b', 'a+b']
# print(re.findall('a[^a-z]b', 'acb adb a3b a*b'))  # ['a3b', 'a*b']

# 练习:
# 找到字符串中'alex_sb ale123_sb wu12sir_sb wusir_sb ritian_sb' 的 alex wusir ritian
# print(re.findall('([a-z]+)_sb','alex_sb ale123_sb wusir12_sb wusir_sb ritian_sb'))


# 分组:

# () 制定一个规则,将满足规则的结果匹配出来
# print(re.findall('(.*?)_sb', 'alex_sb wusir_sb 日天_sb'))  # ['alex', ' wusir', ' 日天']

# 应用举例:
# print(re.findall('href="(.*?)"','<a href="http://www.baidu.com">点击</a>'))#['http://www.baidu.com']


# | 匹配 左边或者右边
# print(re.findall('alex|太白|wusir', 'alex太白wusiraleeeex太太白odlb'))  # ['alex', '太白', 'wusir', '太白']
# print(re.findall('compan(y|ies)','Too many companies have gone bankrupt, and the next one is my company'))  # ['ies', 'y']
# print(re.findall('compan(?:y|ies)','Too many companies have gone bankrupt, and the next one is my company'))  # ['companies', 'company']
# 分组() 中加入?: 表示将整体匹配出来而不只是()里面的内容。
import re

#1 findall 全部找到返回一个列表。
# print(relx.findall('a', 'alexwusirbarryeval'))  # ['a', 'a', 'a']


# 2 search 只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象可以通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None。
# print(relx.search('sb|alex', 'alex sb sb barry 日天'))  # <_sre.SRE_Match object; span=(0, 4), match='alex'>
# print(relx.search('alex', 'alex sb sb barry 日天').group())  # alex


# 3 match:None,同search,不过在字符串开始处进行匹配,完全可以用search+^代替match
# print(relx.match('barry', 'barry alex wusir 日天'))  # <_sre.SRE_Match object; span=(0, 5), match='barry'>
# print(relx.match('barry', 'barry alex wusir 日天').group()) # barry


# 4 split 分割 可按照任意分割符进行分割
# print(relx.split('[ ::,;;,]','alex wusir,日天,太白;女神;肖锋:吴超'))  # ['alex', 'wusir', '日天', '太白', '女神', '肖锋', '吴超']


# 5 sub 替换

# print(relx.sub('barry', '太白', 'barry是最好的讲师,barry就是一个普通老师,请不要将barry当男神对待。'))
# 太白是最好的讲师,太白就是一个普通老师,请不要将太白当男神对待。
# print(relx.sub('barry', '太白', 'barry是最好的讲师,barry就是一个普通老师,请不要将barry当男神对待。',2))
# 太白是最好的讲师,太白就是一个普通老师,请不要将barry当男神对待。
# print(relx.sub('([a-zA-Z]+)([^a-zA-Z]+)([a-zA-Z]+)([^a-zA-Z]+)([a-zA-Z]+)', r'\5\2\3\4\1', r'alex is sb'))
# sb is alex
# 6 # obj=relx.compile('\d{2}') # # print(obj.search('abc123eeee').group()) #12 # print(obj.findall('abc123eeee')) #['12'],重用了obj # import relx # ret = relx.finditer('\d', 'ds3sy4784a') #finditer返回一个存放匹配结果的迭代器 # print(ret) # <callable_iterator object at 0x10195f940> # print(next(ret).group()) #查看第一个结果 # print(next(ret).group()) #查看第二个结果 # print([i.group() for i in ret]) #查看剩余的左右结果

命名分组举例

# 命名分组匹配:
ret = re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>")
# #还可以在分组中利用?<name>的形式给分组起名字
# #获取的匹配结果可以直接用group('名字')拿到对应的值
# print(ret.group('tag_name'))  #结果 :h1
# print(ret.group())  #结果 :<h1>hello</h1>
#
# ret = relx.search(r"<(\w+)>\w+</\1>","<h1>hello</h1>")
# #如果不给组起名字,也可以用\序号来找到对应的组,表示要找的内容和前面的组内容一致
# #获取的匹配结果可以直接用group(序号)拿到对应的值
# print(ret.group(1))
# print(ret.group())  #结果 :<h1>hello</h1>

shutil模块

高级的 文件、文件夹、压缩包 处理模块

shutil.copyfileobj(fsrc, fdst[, length])
将文件内容拷贝到另一个文件中(两个文件必须是已经存在的,不会像学习文件句柄那块‘w’一样,没有会创建)

1 import shutil
2  
3 shutil.copyfileobj(open('old.xml','r'), open('new.xml', 'w'))

shutil.copyfile(src, dst)
拷贝文件

1 shutil.copyfile('f1.log', 'f2.log') #目标文件无需存在

shutil.copymode(src, dst)
仅拷贝权限。内容、组、用户均不变

1 shutil.copymode('f1.log', 'f2.log') #目标文件必须存在

shutil.copystat(src, dst)
仅拷贝状态的信息,包括:mode bits, atime, mtime, flags

1 shutil.copystat('f1.log', 'f2.log') #目标文件必须存在

shutil.copy(src, dst)
拷贝文件和权限

1 import shutil
2  
3 shutil.copy('f1.log', 'f2.log')

shutil.copy2(src, dst)
拷贝文件和状态信息

1 import shutil
2  
3 shutil.copy2('f1.log', 'f2.log')

shutil.ignore_patterns(*patterns)
shutil.copytree(src, dst, symlinks=False, ignore=None)
递归的去拷贝文件夹

1 import shutil
2  
3 shutil.copytree('folder1', 'folder2', ignore=shutil.ignore_patterns('*.pyc', 'tmp*')) #目标目录不能存在,
          注意对folder2目录父级目录要有可写权限,ignore的意思是排除 
拷贝软连接
import shutil

shutil.copytree('f1', 'f2', symlinks=True, ignore=shutil.ignore_patterns('*.pyc', 'tmp*'))

'''
通常的拷贝都把软连接拷贝成硬链接,即对待软连接来说,创建新的文件
'''

shutil.rmtree(path[, ignore_errors[, onerror]])
递归的去删除文件

1 import shutil
2  
3 shutil.rmtree('folder1')

shutil.move(src, dst)
递归的去移动文件,它类似mv命令,其实就是重命名。

1 import shutil
2  
3 shutil.move('folder1', 'folder3')

shutil.make_archive(base_name, format,...)

创建压缩包并返回文件路径,例如:zip、tar

创建压缩包并返回文件路径,例如:zip、tar

    • base_name: 压缩包的文件名,也可以是压缩包的路径。只是文件名时,则保存至当前目录,否则保存至指定路径,
      如 data_bak                       =>保存至当前路径
      如:/tmp/data_bak =>保存至/tmp/
    • format: 压缩包种类,“zip”, “tar”, “bztar”,“gztar”
    • root_dir: 要压缩的文件夹路径(默认当前目录)
    • owner: 用户,默认当前用户
    • group: 组,默认当前组
    • logger: 用于记录日志,通常是logging.Logger对象
复制代码
#将 /data 下的文件打包放置当前程序目录
import shutil
ret = shutil.make_archive("data_bak", 'gztar', root_dir='/data')
  
  
#将 /data下的文件打包放置 /tmp/目录
import shutil
ret = shutil.make_archive("/tmp/data_bak", 'gztar', root_dir='/data')
复制代码

shutil 对压缩包的处理是调用 ZipFile 和 TarFile 两个模块来进行的,详细:

zipfile压缩解压缩
import zipfile

# 压缩
z = zipfile.ZipFile('laxi.zip', 'w')
z.write('a.log')
z.write('data.data')
z.close()

# 解压
z = zipfile.ZipFile('laxi.zip', 'r')
z.extractall(path='.')
z.close()

zipfile压缩解压缩
tarfile压缩解压缩
import tarfile

# 压缩
>>> t=tarfile.open('/tmp/egon.tar','w')
>>> t.add('/test1/a.py',arcname='a.bak')
>>> t.add('/test1/b.py',arcname='b.bak')
>>> t.close()


# 解压
>>> t=tarfile.open('/tmp/egon.tar','r')
>>> t.extractall('/egon')
>>> t.close()

tarfile压缩解压缩

 递归,堆栈以及练习题

放红包

#发红包
#200块钱随机分10份
import random
#基础版本
# ret = random.sample(range(1,200*100),9)
# ret.sort()
# ret.insert(0,0)
# ret.append(200*100)
# l = []
# for i  in range(10):
#     l.append((ret[i+1]-ret[i])/100)
# print(l)
#函数版
# def red_packet(money,num):
#     ret = random.sample(range(1,money*100),num-1)
#     ret.sort()
#     ret.insert(0,0)
#     ret.append(money*100)
#     l = []
#     for i  in range(num):
#         l.append((ret[i+1]-ret[i])/100)
#     return l
# print(red_packet(100,10))
#进阶版
# def red_packet(money,num):
#     ret = random.sample(range(1,money*100),num-1)
#     ret.sort()
#     ret.insert(0,0)
#     ret.append(money*100)
#     return [(ret[i+1]-ret[i])/100 for i  in range(num)]
# print(red_packet(100,10))
#进阶版2.0
# def red_packet(money,num):
#     ret = random.sample(range(1,money*100),num-1)
#     ret.extend([1,money*100])
#     ret.sort()
#     return [(ret[i+1]-ret[i])/100 for i  in range(num)]
# print(red_packet(100,10))
#生成器版
# def red_packet(money,num):
#     ret = random.sample(range(1,money*100),num-1)
#     ret.extend([1,money*100])
#     ret.sort()
#     for i  in range(num):
#         yield  (ret[i + 1] - ret[i]) / 100
#
# print(next(red_packet(100,10)))

计算文件大小

#计算文件夹大小-递归
import os
# def get_size(path):
#     ret = os.listdir(path)
#     total = 0
#     for name in ret:
#         abs_path = os.path.join(path,name)
#         if os.path.isdir(abs_path):
#             #继续计算文件夹中的情况
#             total +=get_size(abs_path)
#         else:
#             total +=os.path.getsize(abs_path)
#     return total
# path = r'D:\pycharmproject\学习记录'
# print(get_size(path))
#堆栈 栈是一种计算机存储数据的思想,先进后出
# import os
# path = r'D:\pycharmproject\学习记录'
# dir_lst =[ path]
# total =0
# while dir_lst:
#     path = dir_lst.pop()
#     ret = os.listdir(path)
#     for name in ret:
#         abs_path = os.path.join(path,name)
#         if os.path.isdir(abs_path):
#             dir_lst.append(abs_path)
#         else:
#             total +=os.path.getsize(abs_path)
# print(total)

三级菜单

#三级菜单
menu = {
    '北京': {
        '海淀': {
            '五道口': {
                'soho': {},
                '网易': {},
                'google': {}
            },
            '中关村': {
                '爱奇艺': {},
                '汽车之家': {},
                'youku': {},
            },
            '上地': {
                '百度': {},
            },
        },
        '昌平': {
            '沙河': {
                '老男孩': {},
                '北航': {},
            },
            '天通苑': {},
            '回龙观': {},
        },
        '朝阳': {},
        '东城': {},
    },
    '上海': {
        '闵行': {
            "人民广场": {
                '炸鸡店': {}
            }
        },
        '闸北': {
            '火车战': {
                '携程': {}
            }
        },
        '浦东': {},
    },
    '山东': {},
}
#递归 #大的套小的 格式差不多一致,这时候就用递归
# def menu3(menu):
#     while True:
#         for key in menu:#北京 上海
#             print(key)
#         inp = input('>>>')
#         if inp in menu:
#             menu3(menu[inp])
#         elif inp =='b':#返回上级菜单
#             return
#         elif inp =='q':
#             exit()
# menu3(menu)

# def menu3(menu): #递归的返回值,需要处理
#     while True:
#         for key in menu:#北京 上海
#             print(key)
#         inp = input('>>>')
#         if inp in menu:
#             ret = menu3(menu[inp])
#             if ret == 'q':return 'q'
#         elif inp =='b':#返回上级菜单
#             return 'b'
#         elif inp =='q':
#             return 'q'
# menu3(menu)
l = [menu]
while l:
    for key in l[-1]:
        print(key)
    inp = input('>>>')
    if inp in l[-1]:
        l.append(l[-1][inp])
        continue
    elif inp == 'b':
        l.pop()
    elif inp == 'q':
        break

 

 

 






 

posted @ 2020-02-20 21:43  我是小旋风  阅读(440)  评论(0)    收藏  举报