python之常用模块 python之常用模块

标签(空格分隔): python


什么是模块?

  • 在计算机程序开发的过程中,随着程序代码越写越多,在一个文件里代码就会越来越长,越来越不容易维护,为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里面,这样每个文件里面包含的代码就相对较少了,很多的编程语言都采用这种组织代码的方式,在python中,一个.py文件就是一个模块;

使用模块的好处?

  • 1.最大的好处就是大大提高了代码的可维护性,其次,编写代码不必从零开始,当一个模块编写完毕了,就可以被其他的模块引用,我们在编写程序的时候,也经常引用其他的模块,包括python 内置模块和来自第三方的模块,
    2.使用模块可以避免函数名和变量的冲突,每个模块有独立的命名空间,因此相同名字的函数和变量完全可以分别在不同的模块中,所以,我们自己在编写模块时,不必考虑名字会与其他的模块的冲突;

模块分类

模块分为3类:
1.内置标准模块,又称为:标准库,执行(help'modules')查看所有的python自带模块列表;
2.第三方开源模块,可通过pip install 模块名,联网安装;
3.自定义模块;

模块的调用

import module
from module import xx
from module.xx.xx import xx as rename
from module.xx.xx import *
  • 注意:模块一旦被调用,即相当于执行了另外一个py文件里面的代码;

自定义模块:

这个最简单,创建一个.py文件,就可以称之为模块,就可以在另一个程序里面导入了;

开源模块的学习安装方式:

pip install xx

time 模块详解

本节重点:time and datetime 的使用

  • 在平时的代码中,我们常用需要和时间打交道,在Python中与时间打交道的就是:time,datetime,calendar(很少用)下面分别来介绍:

在python中通常有这几种方式来表示时间:
1.时间戳
2.格式化时间字符串
3.元组(struct_time)共九个元素,由于python的time模块实现主要调用C库,所以各个平台可能有所不同;

几个定义:

1.UTC(Coordinated Universal Time,世界协调时)亦即格林威治天文时间,世界标准时间。在中国为UTC+8。DST(Daylight Saving Time)即夏令时。

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

3.元组(struct_time)方式:struct_time元组共有9个元素,返回struct_time的函数主要有gmtime(),localtime(),strptime()。下面列出这种方式元组中的几个元素:

python之常用模块
python之常用模块

time 模块和方法:

  • time.localtime([secs]):将一个时间戳转换为当前时区的struct_time,secs参数未提供,则以当前时间为准;
import time
print(time.localtime())

执行结果:time.struct_time(tm_year=2018, tm_mon=6, tm_mday=25, tm_hour=22, tm_min=13, tm_sec=35, tm_wday=0, tm_yday=176, tm_isdst=0)

a=time.localtime()
b='%s-%s-%s'%(a.tm_year,a.tm_mon,a.tm_mday)
print(b)

执行结果:
'2018-6-25'

  • time.gmtime([secs]):和localtime()方法类似,gmtime()方法是将一个时间戳转换为UTC时区(0时区)的struct_time。
a=time.gmtime()
print(a)

执行结果:
time.struct_time(tm_year=2018, tm_mon=6, tm_mday=25, tm_hour=14, tm_min=19, tm_sec=40, tm_wday=0, tm_yday=176, tm_isdst=0)
方法不传值,默认打印的是当前的时间;
如下的代码

a=time.localtime(14700032232)
print(a)

执行结果:会有对应的时间打印出来

  • time.time():返回当前时间的时间戳
  • time.mktime():将一个struct_time转化为时间戳
  • time.sleep(secs):线程推迟指定的时间运行,单位为秒
  • tiem.asctime([t]):把一个时间的元组或者struct_time表示为这种形式:sun oct 1 12:04:38 2017,如果没有参数,将会将time.localtime()作为参数传入;
  • time.ctime([secs]):把一个时间戳(秒)转化为time.asctime()的形式,如果参数未给,或者为None的时候,将会默认time.time()作为参数,他的作用相当于time.asctime(time.localtime(secs))
  • time.strftime(format[,t]):把一个代表时间的元组或者struct_time(如由time.localtime()和time.gmtime()返回)转化为格式化的时间字符串,如果t未指定,将会传入,time.localtime()
time.strftime('2017-8')
time.strftime('%Y-%m-%d %H:%M:%S',)
time.strftime('%Y-%m-%d %H:%M:%S %p',)#上午下午
time.strftime('%Y-%m-%d %H:%M:%S %U',)#这个一年的多少周
time.strftime('%Y-%m-%d %H:%M:%S %w',)#这个是周几

python之常用模块
python之常用模块

datetime模块

相对于time模块,datetime模块的接口直观,更加容易调用;
datetime模块定义了如下的几个类:

  • datetime.date:表示日期的类,常用的属性有:year month,day
  • datetime.time:表示时间的类,常用的属性有:hour,minute,second,microsecond
  • datetime.datetime :表示日期时间
  • datetime.timedelta:表示时间间隔,即两个时间点之间的长度
  • datetime.tzinfo 与时区有关的相关信息,(这里不再赘述,有兴趣的同学自行查看)
我们需要记住的:

1.d=datetime.datetime.now()返回当前的datetime日期类型

import datetime
d=datetime.datetime。now()
print(d)
print(d.year)
print(d.today())
print(d.timestamp())
print(d.timetuple())

等方法可以调用
2.datetime.formtimestamp(322222)把一个时间戳转换为datetime 日期类型;

datetime.date.fromtimestamp(time.time())

3.时间运算:

datetime.datetime.now()+datetime.timedelta(4)#当前时间+4天
datetime.datetime.now()+datetimetimedelta(hours=4)#当前时间+4小时

4.时间的替换

d=datetime.datetime.now()
d.replace(year=2999,month=11,day=30)
datetime.date(2999,11,30)

random 随机数

程序中有很多的地方需要用到随机字符,比如登录网站的验证码,通过random模块可以很容易的生成随机字符串

random.randrange(1,10)#返回1-10之间的一个随机数,不包括10
random.randint(1,10)#返回1-10之间的一个随机数,包括10
random.randrange(0,100,2)#随机取0到100之间的偶数
random.random()#返回一个随机浮点数
random.choice('abc#$@1')#返回一个给定数据集合中的随机字符
random.sample('abcdefghij',3)#从多个字符串中选取特定数量的字符
#生成随机的字符串
import string
''.json(random.sample(string.ascii_lowercase+string.digits,6))
#洗牌
a=[0,1,2,3,4,5,6,7,8,9]
random.shuffle(a)
print(a)

os模块

1.得到当前的工作路径,即当前python脚本工作的路径:os.getcwd(),注意这里是python解释器的路径,不是你.py文件的路径

import os
print(os.getcwd())

2.返回指定目录下的所有文件和目录名:os.listdir()

#返回当前目录下的所有文件和目录名
print(os.listdir('.'))

3.函数用来删除一个文件:os.remove()

4.删除多个目录:os.removedirs()

os.removedirs(r"c:python")

5.检验给出的路径是不是一个文件:os.path.isfile()
返回值是true or false

6.检验给出的路径是否是一个目录:os.path.isdir()
返回值是true or false

7.判断是否是绝对路径:os.path.exists()
返回是true or false

8.返回一个路径的目录名和文件名:os.path.split()

os.path.split('/home/haha/lele/test.txt')

执行结果是:
('/home/haha/lele/','test.txt')
9.分离扩展名:os.path.splitext()

os.path.splittext('/home/haha/lele/test.txt')

执行结果:('/home/haha/lele/test','.py')
10.获取路径名字:os.path.dirname()
11.获取绝对路径:os.path.abspath()
12.获取文件名:os.paht.basename()
13.运行shell命令:os.system()

os.system('df-h')#查看磁盘

14.读取操作系统环境变量HOME的值:os.getenv('HOME')
15.返回操作系统所有的环境变量的值:os.environ
16.设置环境变量,仅仅程序运行的时候有效果:
os.environ.setdefault('HOME','/home/mimi')
17.给出当前平台使用的终止符:os.linesep

  • windwos 使用的是:' ',Linux使用的是:' ',mac使用的是' '

18.指出你正在使用的平台:os.name

  • windows 使用的是:'nt',而对于linux用户,他是:'posix'

19.重命名:os.rename(old_name,new_name)
20.创建多级目录:os.makedirs(r'c:python est')
21.创建单个目录:os.mkdir('test')
22.获取文件的属性:os.stat(file)
23.终止当前进程:os.exit()
24.获取文件的大小:os.path.getsize(filename)
25.修改文件的权限和时间戳:os.chmod(file)
26.结合目录和文件名:os.path.join(dir,filename)
27.改变工作目录到dirname:os.chdir(dirname)
28.获取当前终端的大小:os.get_terminal_size()
29.杀死进程:os.kill(10884,signal.SIGKILL)

sys模块的使用

1.sys.argv

python3 test.py run web 

执行结果:
['test.py','run','web']
这个意思就是有的脚本后边可以跟参数,如果跟run我们就执行run,如果跟web 我们就执行web
2.sys.exit(n)退出程序,正常退出
3.sys.version 获取当前解释器的版本
4.sys.maxint 最大的Int值
5.sys.path 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
6.sys.platform 返回操作平台的名称
7.sys.getrecursionlimit()获取递归的最大层数
8.sys.setrecursionlimit(1200) 设置最大递归层数
9.sys.getdefaultencoding() 获取解释器默认编码
10.sys.getfilesystemencoding 获取内存数据存到文件里的默认编码

shutil模块

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

  • shutil.copyfileobj(fsrc,fdst[,llength])
    将文件内容拷贝到另一个文件中,可以部分内容
    源码如下:
def copyfileobj(fsrc, fdst, length=16*1024):#这里是每次读多少,并不是拷贝多少长度
    """copy data from file-like object fsrc to file-like object fdst"""
    while 1:
        buf = fsrc.read(length)#这里是每次读多少,并不是拷贝多少长度
        if not buf:
            break
        fdst.write(buf)

演示:
新建一个文件叫sheve_test.py,内容如下图所示并保存:
python之常用模块
python之常用模块

import shutil

f1=open("sheve_test.py",'r')
f2=open("sheve_test_new.py",'w')
shutil.copyfileobj(f1,f2)

然后新建一个.py文件用来编写脚本,copy文件的

import shutil

f1=open("sheve_test.py",'r')
f2=open("sheve_test_new.py",'w')
shutil.copyfileobj(f1,f2)

如上述代码执行完毕后,会生成一个文件:sheve_test_new.py文件,在这个文件里面,会有和sheve_test.py文件一样的内容;如上图文件;

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

源码如下:

def copyfile(src, dst):
    """Copy data from src to dst"""
    if _samefile(src, dst):
        raise Error("`%s` and `%s` are the same file" % (src, dst))

    for fn in [src, dst]:
        try:
            st = os.stat(fn)
        except OSError:
            # File most likely does not exist
            pass
        else:
            # XXX What about other special files? (sockets, devices...)
            if stat.S_ISFIFO(st.st_mode):
                raise SpecialFileError("`%s` is a named pipe" % fn)

    with open(src, 'rb') as fsrc:
        with open(dst, 'wb') as fdst:
            copyfileobj(fsrc, fdst)
  • shutil.copymode(src, dst)
    仅拷贝权限。内容、组、用户均不变

源码如下:

def copymode(src, dst):
    """Copy mode bits from src to dst"""
    if hasattr(os, 'chmod'):
        st = os.stat(src)
        mode = stat.S_IMODE(st.st_mode)
        os.chmod(dst, mode)
        
  • shutil.copystat(src, dst)
    拷贝状态的信息,包括:mode bits, atime, mtime, flags

源码如下:

def copystat(src, dst):
    """Copy all stat info (mode bits, atime, mtime, flags) from src to dst"""
    st = os.stat(src)
    mode = stat.S_IMODE(st.st_mode)
    if hasattr(os, 'utime'):
        os.utime(dst, (st.st_atime, st.st_mtime))
    if hasattr(os, 'chmod'):
        os.chmod(dst, mode)
    if hasattr(os, 'chflags') and hasattr(st, 'st_flags'):
        try:
            os.chflags(dst, st.st_flags)
        except OSError, why:
            for err in 'EOPNOTSUPP', 'ENOTSUP':
                if hasattr(errno, err) and why.errno == getattr(errno, err):
                    break
            else:
                raise
  • shutil.copy(src, dst)
    拷贝文件和权限

源码如下:

def copy(src, dst):
    """Copy data and mode bits ("cp src dst").

    The destination may be a directory.

    """
    if os.path.isdir(dst):
        dst = os.path.join(dst, os.path.basename(src))
    copyfile(src, dst)
    copymode(src, dst
  • shutil.copy2(src, dst)
    拷贝文件和状态信息

源码如下:

def copy2(src, dst):
    """Copy data and all stat info ("cp -p src dst").

    The destination may be a directory.

    """
    if os.path.isdir(dst):
        dst = os.path.join(dst, os.path.basename(src))
    copyfile(src, dst)
    copystat(src, dst)
  • shutil.ignore_patterns(*patterns)
    shutil.copytree(src, dst, symlinks=False, ignore=None)
    递归的去拷贝文件

例如:copytree(source, destination, ignore=ignore_patterns('.pyc', 'tmp'))

  • shutil.rmtree(path[, ignore_errors[, onerror]])
    递归的去删除文件
  • shutil.move(src, dst)
    递归的去移动文件

序列化模块:

什么是序列化?

序列化,是指吧内存里的数据类型转化成为字符串,以使其能存储到硬盘或者通过网络传输到远程,因为硬盘或者网络传输之恩能够接受bytes

为什么要序列化?

你打游戏的时候,打累了,休息了2天,关掉游戏,结果你下次开始还是从你上次的地方接着打,那么你上次玩游戏的进度肯定是存在硬盘上了,是以何种形式呢?
游戏过程中产生的很多临时数据是不规律的,可能在你关掉,游戏的时候正好有10个列表,3个嵌套字典数据,集合在内存里面,需要存下来,你如何存呢?把列表变成文件里的多行多列的形式?那嵌套字典呢?根本没办法存,所以,若是有种办法可以直接把内存数据存到硬盘上,下次程序启动,在从硬盘上读回来,还是原来的格式化,自然是极好的办法;

  • 用于序列化的两个模块:
    json,用于字符串和python数据类型间进行转换;
    pickle,用于python特有的类型和python的数据类型间进行转换;
    python之常用模块
python之常用模块
data = {'roles': [

    {'role': 'monster', 'type': 'pig', 'life': 50},
    {'role': 'hero', 'type': '关羽', 'life': 80},
]}

f = open("game_status", 'w')
f.write(data)

执行结果:
Traceback (most recent call last):
File "G:/fancy/misc/shuiltdemo.py", line 8, in
f.write(data)
TypeError: write() argument must be str, not dict

Process finished with exit code 1

报错了,信息显示必须要接收str类型;

data = {'roles': [

    {'role': 'monster', 'type': 'pig', 'life': 50},
    {'role': 'hero', 'type': '关羽', 'life': 80},
]}

f = open("game_status", 'w')
f.write(str(data))

执行以上结果就ok了不会报错了;

  • 紧接着上述的步骤走:
    把创建的game_status文件的内容读出来:
f = open("game_status",'r')
d=f.read()
print(d['roles'])

执行结果:
Traceback (most recent call last):
File "G:/fancy/misc/shuiltdemo.py", line 12, in
print(d['roles'])
TypeError: string indices must be integers

结果报错,因为d是str类型的

这里就需要我们把str变为字典,那我们采取如下的手段:

d=dict(d)

执行结果:
d=dict(d)
ValueError: dictionary update sequence element #0 has length 1; 2 is required
结果还是不行;

  • 如上的问题就是字典转成字符串,在反转就转不会来了,这就是这个问题,那我们怎么办呢?
  • 这里我们之前学过:eval()
f = open("game_status",'r')
d=f.read()
d=eval(d)
print(d['roles'])

执行结果为:
[{'role': 'monster', 'type': 'pig', 'life': 50}, {'role': 'hero', 'type': '关羽', 'life': 80}]

  • 把内存数据转成字符,叫做序列化
    把字符转成内存数据,叫做反序列化;
    刚刚的代码做了两件事情:1.是把字典变成字符串,2.是把字符串eval()一下,转换为内存数据;
  • 其实在python里面有自带的模块专门干这个功能的;
    1.pickle 模块
    2.json 模块
    这两个模块的主要功能就是内存的数据很容易存下来,也很容易读出来;
    这两个模块的用法完全一样;
  • json:
    1.dumps()
import json

data = {'roles': [

    {'role': 'monster', 'type': 'pig', 'life': 50},
    {'role': 'hero', 'type': '关羽', 'life': 80},
]}
d=json.dumps(data)#
print(d,type(d))

执行结果:
{"roles": [{"role": "monster", "type": "pig", "life": 50}, {"role": "hero", "type": "u5173u7fbd", "life": 80}]} <class 'str'>

如上结果可以看出,他已经是个字符串了;

2.dump()
这个和dumps()其实差不多,只是dumps()只传入数据的参数,但是dump()要传入数据和文件;

import json
data = {'roles': [
    {'role': 'monster', 'type': 'pig', 'life': 50},
    {'role': 'hero', 'type': '关羽', 'life': 80},
]}
json.dump(data,'test.json')#仅仅转成字符串

执行结果:
AttributeError: 'str' object has no attribute 'write'
报错了,

  • 这里申明一下:dump()在传入文件的时候,文件要是一个可以写的对象,因为他不能自己打开文件;
import json
data = {'roles': [
    {'role': 'monster', 'type': 'pig', 'life': 50},
    {'role': 'hero', 'type': '关羽', 'life': 80},
]}
f = open("test.json",'w')#增加些的权限,并且打开不文件
json.dump(data,f)#转成字符串并写入文件

如上述,执行结果不会报错了,正常出现了test.json文件
python之常用模块
python之常用模块

  • loads()
import json
data = {'roles': [
    {'role': 'monster', 'type': 'pig', 'life': 50},
    {'role': 'hero', 'type': '关羽', 'life': 80},
]}
d=json.dumps(data)
d2=json.loads(d)
print(d2['roles'])

执行结果:
[{'role': 'monster', 'type': 'pig', 'life': 50}, {'role': 'hero', 'type': '关羽', 'life': 80}]

*load()
以下例子是打开刚刚生成的json文件

 f =open('test.json','r')
    data=json.load(f)
    print(data)

执行结果为:
{'roles': [{'role': 'monster', 'type': 'pig', 'life': 50}, {'role': 'hero', 'type': '关羽', 'life': 80}]}

1.把数据类型转化为字符串存到内存的意义?
json.dumps json.loads
a.把你的内存数据,通过网络,共享给远程其它人;
b.可以跨平台,跨语言共享你的数据;
c.定义了不同语言的之前的交互规则
纯文本:坏处:不能共享给远程其他的人;
xml:坏处:占用的空间大
json:简单,可读性好

  • 刚才我只是把这个数据dumps()了一次,能不能dump()多次呢?
    答案:
    是可以的;这里大家自行实验;
    那可以对于dump()多次的,可以load()吗?,可以load()多次吗

答案:
在load()的时候会报错的,load()一次就会报错,load()多次也会报错的;

  • 这里注意,dump()了几次,然后,load()第一次就会报错,所以,我们在使用dump()和load()的时候不要反复的dump()和load()

总结:
json:的方法:
dumps()------------loads()
dump()--------------load()

pickle模块

pickle和json的用法是一致的:
例子:如下代码:

import pickle

d = {'name': 'alex', 'age': 22}
l = [1, 2, 3, 'rain']
pk = open('data.pkl','w')
print(pickle.dumps(d))

执行结果:
b'x80x03}qx00(Xx04x00x00x00nameqx01Xx04x00x00x00alexqx02Xx03x00x00x00ageqx03Kx
它会先转化为bytes类型;

import pickle

d = {'name': 'awaw', 'age': 22}
l = [1, 2, 3, 'rain']
pk = open('data.pkl', 'wb')
pickle.dump(d, pk)

python之常用模块
python之常用模块

那么反序列化呢:

f=open('data.pkl','rb')
d=pickle.load(f)
print(d)

打开上图的文件,然后进行load()就可以了
执行结果为:
{'name': 'awaw', 'age': 22}

  • json 和pickle的区别呢?
    json:支持:str int,tuple,list,dict,不支持set
    pickle:支持:python里面的所有的数据类型,包括函数;
    2.这样说来,pickle更牛逼,我们以后就用pickle好了;
    答案不行:
    pickle之能在python里面用,不能跨平台,跨语言;(所以说没用不用研究)

shelve模块

之前说json和pickle只能dump,load一次,假如我确实有好几种数据需要序列化,如果只能dump一次的话,这就意味着我自己要dump好几个文件,这个时候就感觉很low,难道没有办法允许我dump好几次吗?
shelve他是对pickle的封装,允许你dump多次,load多次,并且不会顺序乱,因为他是一种key-value的形式;(这个下去自己研究,这里不在多说了,因为它主要是对pickle封装的)

configparser 模块

很多软件都有配置文件,来配置一些参数;
编写一个配置文件叫:conf.ini
内容如下:

[DEFAULT]
ServerAliveInterval =45
Compression = yes
CompressionLevel = 9
ForwardX11 = yes
[bitbucket.org]
User = hg

[topsecret.server.com]
Port=50022
ForwardX11 = no

然后进行如下的操作:

import configparser
conf=configparser.ConfigParser()
# print(conf.sections())
conf.read('conf.ini')
print(conf.default_section)#打印配置文件的deaulf
print(dir(conf["bitbucket.org"]))#打印bitbucket.org的值

for k,v in conf["bitbucket.org"].items():
    print(k,v)#这时候default的内容也会展示出来,默认default会在后续的节点都会展示出来

增删改查,自己行研究;

hashlib模块:

在讲解这个模块之前先给大家说一些,关于加密的一些知识:
HASH

  • Hash,一般翻译做“散列”,也有直接音译为”哈希”的,就是把任意长度的输入(又叫做预映射,pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。
  • 简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
  • HASH主要用于信息安全领域中加密算法,他把一些不同长度的信息转化成杂乱的128位的编码里,叫做HASH值.也可以说,hash就是找到一种数据内容和数据存放地址之间的映射关系

MD5

  • 输入任意长度的信息,经过处理,输出为128位的信息(数字指纹);不同的输入得到的不同的结果(唯一性);
  • MD5算法的特点
    1.压缩性:任意长度的数据,算出的MD5值的长度都是固定的
    2.容易计算:从原数据计算出MD5值很容易
    3.抗修改性:对原数据进行任何改动,修改一个字节生成的MD5值区别也会很大
    4.强抗碰撞:已知原数据和MD5,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。

MD5算法是否可逆?

  • MD5不可逆的原因是其是一种散列函数,使用的是hash算法,在计算过程中原文的部分信息是丢失了的。

MD5用途

1.防止被篡改:
比如发送一个电子文档,发送前,我先得到MD5的输出结果a。然后在对方收到电子文档后,对方也得到一个MD5的输出结果b。如果a与b一样就代表中途未被篡改。

比如我提供文件下载,为了防止不法分子在安装程序中添加木马,我可以在网站上公布由安装文件得到的MD5输出结果。

SVN在检测文件是否在CheckOut后被修改过,也是用到了MD5.

2.防止直接看到明文:
现在很多网站在数据库存储用户的密码的时候都是存储用户密码的MD5值。这样就算不法分子得到数据库的用户密码的MD5值,也无法知道用户的密码。(比如在UNIX系统中用户的密码就是以MD5(或其它类似的算法)经加密后存储在文件系统中。当用户登录的时候,系统把用户输入的密码计算成MD5值,然后再去和保存在文件系统中的MD5值进行比较,进而确定输入的密码是否正确。通过这样的步骤,系统在并不知道用户密码的明码的情况下就可以确定用户登录系统的合法性。这不但可以避免用户的密码被具有系统管理员权限的用户知道,而且还在一定程度上增加了密码被破解的难度。)

3.防止抵赖:(数字签名)
这需要一个第三方认证机构。例如A写了一个文件,认证机构对此文件用MD5算法产生摘要信息并做好记录。若以后A说这文件不是他写的,权威机构只需对此文件重新产生摘要信息,然后跟记录在册的摘要信息进行比对,相同的话,就证明是A写的了。这就是所谓的“数字签名”。

SHA-1

安全哈希算法(Secure Hash Algorithm)主要适用于数字签名标准(Digital Signature Standard DSS)里面定义的数字签名算法(Digital Signature Algorithm DSA)。对于长度小于2^64位的消息,SHA1会产生一个160位的消息摘要。当接收到消息的时候,这个消息摘要可以用来验证数据的完整性。

SHA是美国国家安全局设计的,由美国国家标准和技术研究院发布的一系列密码散列函数。

由于MD5和SHA-1于2005年被山东大学的教授王小云破解了,科学家们又推出了SHA224, SHA256, SHA384, SHA512,当然位数越长,破解难度越大,但同时生成加密的消息摘要所耗时间也更长。目前最流行的是加密算法是SHA-256 .

MD5与SHA-1的比较

由于MD5与SHA-1均是从MD4发展而来,它们的结构和强度等特性有很多相似之处,SHA-1与MD5的最大区别在于其摘要比MD5摘要长32 比特。对于强行攻击,产生任何一个报文使之摘要等于给定报文摘要的难度:MD5是2128数量级的操作,SHA-1是2160数量级的操作。产生具有相同摘要的两个报文的难度:MD5是264是数量级的操作,SHA-1 是280数量级的操作。因而,SHA-1对强行攻击的强度更大。但由于SHA-1的循环步骤比MD5多80:64且要处理的缓存大160比特:128比特,SHA-1的运行速度比MD5慢.

Python的 提供的相关模块:

用于加密相关的操作,3.x里代替了md5模块和sha模块,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法

import hashlib

m = hashlib.md5()
m.update(b"Hello")
m.update(b"It's me")
print(m.digest())
m.update(b"It's been a long time since last time we ...")

print(m.digest()) #2进制格式hash
print(len(m.hexdigest())) #16进制格式hash
'''
def digest(self, *args, **kwargs): # real signature unknown
    """ Return the digest value as a string of binary data. """
    pass

def hexdigest(self, *args, **kwargs): # real signature unknown
    """ Return the digest value as a string of hexadecimal digits. """
    pass

'''
import hashlib

# ######## md5 ########

hash = hashlib.md5()
hash.update('admin')
print(hash.hexdigest())

# ######## sha1 ########

hash = hashlib.sha1()
hash.update('admin')
print(hash.hexdigest())

# ######## sha256 ########

hash = hashlib.sha256()
hash.update('admin')
print(hash.hexdigest())


# ######## sha384 ########

hash = hashlib.sha384()
hash.update('admin')
print(hash.hexdigest())

# ######## sha512 ########

hash = hashlib.sha512()
hash.update('admin')
print(hash.hexdigest())

例如如下例子:

import hashlib
print(hash('wang'))

执行结果为:
3991323452314028184

  • 注意只要程序不退出,wang的所有的hash都是这个字符串,一旦退出重新进入程序,重新编译,就不是这样的结果了;
  • hash 是不可逆的,即使知道了:3991323452314028184也不能反推出来:wang
    所以常被用于各个的加密网站,是加密的基础的东西;

subrpocess模块

  • 我们经常需要通过Python去执行一条系统命令或脚本,系统的shell命令是独立于你的python进程之外的,每执行一条命令,就是发起一个新进程,通过python调用系统命令或脚本的模块在python2有os.system
import os
os.system('uname')

执行结果(小编是Mac):
Darwin
0
这个代码就是执行系统的命令,其中0就是执行的状态,每个系统都是有状态,Linux就是成功:0,如果是非零就是说明失败;
linux中通过:echo $?可以查看上一个命令执行的一个状态;

import os
a=os.sytem('df')
print(a)# a=0这里a是上次执行的一个状态;

通过上述的代码我是拿不到os.system('df')输出 的结果的,如果我想拿到怎么办呢?
os.popen('df -h')
执行结果:<os._wrap_close object at 0x103a14588>
相当于在内存里面打开了一个临时文件,把内容存到临时文件里面;so,我们只需要把这个文件读出来就好了;

import os
os.system('df')
f=os.popen('df -h')
f.read()

so:我们需要学习subprocess
之前调用操作系统,执行命令的方法很多种,python2也有很多种,例如:commands,python3清理一些不规范的东西,把之前的东西规范化,subsprocess替换了别的,规范化;

  • 三种执行命令的方法:
1.subprocess.run(*popenargs, input=None, timeout=None, check=False, **kwargs) #官方推荐
2. subprocess.call(*popenargs, timeout=None, **kwargs) #跟上面实现的内容差不多,另一种写法
3.subprocess.Popen() #上面各种方法的底层封装

run()方法:

  • 执行这个命令,返回一个对象,拿着对象执行命令;
    1.标准写法:
    subprocess.run(['df','-h'],stderr=subprocess.PIPE,stdout=subprocess.PIPE,check=True)
    2.涉及到管道|的命令需要这样写
    subprocess.run('df -h|grep disk1',shell=True) #shell=True的意思是这条命令直接交给系统去执行,不需要python负责解析

call()方法:

#执行命令,返回命令执行状态 , 0 or 非0
>>> retcode = subprocess.call(["ls", "-l"])
#执行命令,如果命令结果为0,就正常返回,否则抛异常
>>> subprocess.check_call(["ls", "-l"])
0
#接收字符串格式命令,返回元组形式,第1个元素是执行状态,第2个是命令结果 
>>> subprocess.getstatusoutput('ls /bin/ls')
(0, '/bin/ls')
#接收字符串格式命令,并返回结果
>>> subprocess.getoutput('ls /bin/ls')
'/bin/ls'
#执行命令,并返回结果,注意是返回结果,不是打印,下例结果返回给res
>>> res=subprocess.check_output(['ls','-l'])
>>> res
b'total 0
drwxr-xr-x 12 alex staff 408 Nov 2 11:05 OldBoyCRM
'

Popen()方法:

常用参数:

  • args:shell命令,可以是字符串或者序列类型(如:list,元组)
    stdin, stdout, stderr:分别表示程序的标准输入、输出、错误句柄
    preexec_fn:只在Unix平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用
    shell:同上
    cwd:用于设置子进程的当前目录
    env:用于指定子进程的环境变量。如果env = None,子进程的环境变量将从父进程中继承。
    下面这2条语句执行会有什么区别?
    a=subprocess.run('sleep 10',shell=True,stdout=subprocess.PIPE)
    a=subprocess.Popen('sleep 10',shell=True,stdout=subprocess.PIPE)
    区别是Popen会在发起命令后立刻返回,而不等命令执行结果。这样的好处是什么呢?
    如果你调用的命令或脚本 需要执行10分钟,你的主程序不需卡在这里等10分钟,可以继续往下走,干别的事情,每过一会,通过一个什么方法来检测一下命令是否执行完成就好了。
    Popen调用后会返回一个对象,可以通过这个对象拿到命令执行结果或状态等,该对象有以下方法
    poll()
    Check if child process has terminated. Returns returncode
    wait()
    Wait for child process to terminate. Returns returncode attribute.
    terminate()终止所启动的进程Terminate the process with SIGTERM
    kill() 杀死所启动的进程 Kill the process with SIGKILL
    communicate()与启动的进程交互,发送数据到stdin,并从stdout接收输出,然后等待任务结束

a = subprocess.Popen('python3 guess_age.py',stdout=subprocess.PIPE,stderr=subprocess.PIPE,stdin=subprocess.PIPE,shell=True)
a.communicate(b'22')
(b'your guess:try bigger ', b'')
send_signal(signal.xxx)发送系统信号
pid 拿到所启动进程的进程号

logging模块:

logging模块就是为了帮助我们记录日志,记录日志,为了以后分析所用,或者除了问题可以查看;

  • python的logging模块提供了标准的日志接口,你可以通过它存储各种格式的日志,logging的日志可以分为: debug(),info(), warning(),error()
    and critical()5个级别,下面我们看一下怎么用?

最简单的用法:

import logging
logging.warning('i am logging 3 times')
logging.critical('server down')

执行结果:是输出到屏幕的
WARNING:root:i am logging 3 times
CRITICAL:root:server down

  • 看一下日志级别:
    python之常用模块
python之常用模块

如果要把日志写到文件里呢?其实也简单:

import logging
logging.basicConfig(filename='example.log',level=logging.INFO)
logging.debug('This message shouldn't go to the log file')
logging.info('So should this')
logging.warning('And this, too')

其中下面这句中的level=loggin.INFO意思是,把日志纪录级别设置为INFO,也就是说,只有比日志是INFO或比INFO级别更高的日志才会被纪录到文件里,在这个例子, 第一条日志是不会被纪录的,如果希望纪录debug的日志,那把日志级别改成DEBUG就行了

自定义日志格式,加入时间:

import logging
logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
logging.warning('is when this event was logged.')

#输出
12/12/2010 11:46:36 AM is when this event was logged.

除了加时间,还可以自定义一大堆格式,下表就是所有支持的格式:
python之常用模块
python之常用模块

logging模块的高级阶段:

  • 如果一条日志同时向屏幕和文件打印怎么办?
    这里我们需要了解logging模块的复杂知识:
    logging有4个主要的类:
    1.logger:提供了应用程序直接使用的接口;
    2.handler:将logger创建的日志发送到合适的目的地:
    3.filter:提供过滤,来决定哪些日志需要输出;
    4.formatter决定日志记录的最终输出格式;

他们之间的关系如图:
python之常用模块
python之常用模块

每个组件的功能是:

logger

  • 每个程序在输出信息之前都要获得一个Logger。Logger通常对应了程序的模块名,比如聊天工具的图形界面模块可以这样获得它的Logger:
LOG=loggin.getlogger('chat.gui')

而核心模块可以这样设置:

LOG=logging.getlogger('chat.kernel')

还可以绑定handler和filters:

  • Logger.setLevel(lel):指定最低的日志级别,低于lel的级别将被忽略。debug是最低的内置级别,critical为最高
    Logger.addFilter(filt)、Logger.removeFilter(filt):添加或删除指定的filter
    Logger.addHandler(hdlr)、Logger.removeHandler(hdlr):增加或删除指定的handler
    Logger.debug()、Logger.info()、Logger.warning()、Logger.error()、Logger.critical():可以设置的日志级别

handler

  • handler对象负责发送相关的信息到指定目的地。Python的日志系统有多种Handler可以使用。有些Handler可以把信息输出到控制台,有些Handler可以把信息输出到文件,还有些 Handler可以把信息发送到网络上。如果觉得不够用,还可以编写自己的Handler。可以通过addHandler()方法添加多个多handler
  • Handler.setLevel(lel):指定被处理的信息级别,低于lel级别的信息将被忽略
    Handler.setFormatter():给这个handler选择一个格式
    Handler.addFilter(filt)、Handler.removeFilter(filt):新增或删除一个filter对象

每个Logger可以附加多个Handler。接下来我们就来介绍一些常用的Handler:

1.logging.StreamHandler 使用这个Handler可以向类似与sys.stdout或者sys.stderr的任何文件对象(file object)输出信息。
2.logging.FileHandler 和StreamHandler 类似,用于向一个文件输出日志信息。不过FileHandler会帮你打开这个文件
3.logging.handlers.RotatingFileHandler

这个Handler类似于上面的FileHandler,但是它可以管理文件大小。当文件达到一定大小之后,它会自动将当前日志文件改名,然后创建 一个新的同名日志文件继续输出。比如日志文件是chat.log。当chat.log达到指定的大小之后,RotatingFileHandler自动把 文件改名为chat.log.1。不过,如果chat.log.1已经存在,会先把chat.log.1重命名为chat.log.2。。。最后重新创建 chat.log,继续输出日志信息。它的函数是:

  • RotatingFileHandler( filename[, mode[, maxBytes[, backupCount]]])
    其中filename和mode两个参数和FileHandler一样。
  • maxBytes用于指定日志文件的最大文件大小。如果maxBytes为0,意味着日志文件可以无限大,这时上面描述的重命名过程就不会发生。
  • backupCount用于指定保留的备份文件的个数。比如,如果指定为2,当上面描述的重命名过程发生时,原有的chat.log.2并不会被更名,而是被删除。

4.logging.handlers.TimedRotatingFileHandler
这个Handler和RotatingFileHandler类似,不过,它没有通过判断文件大小来决定何时重新创建日志文件,而是间隔一定时间就 自动创建新的日志文件。重命名的过程与RotatingFileHandler类似,不过新的文件不是附加数字,而是当前时间。它的函数是:

  • TimedRotatingFileHandler( filename [,when [,interval [,backupCount]]])

其中filename参数和backupCount参数和RotatingFileHandler具有相同的意义。
interval是时间间隔。
when参数是一个字符串。表示时间间隔的单位,不区分大小写。它有以下取值:

S 秒
M 分
H 小时
D 天
W 每星期(interval==0时代表星期一)
midnight 每天凌晨

formatter组件:

日志的formatter是个独立的组件,可以跟handler组合

  • fh = logging.FileHandler("access.log")
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    fh.setFormatter(formatter) #把formmater绑定到fh上

filter 组件:过滤日志:

class IgnoreBackupLogFilter(logging.Filter):
    """忽略带db backup 的日志"""
def filter(self, record): #固定写法
    return   "db backup" not in record.getMessage()

filter 返回true或者false,logger 根据这个进行判断是否输出日志;
例如:

import logging
#生成logger对象:
logger= logging.getLogger('web')
logger.setLevel(logging.INFO)
#生成hander对象
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
fh = logging.FileHandler('web.log')
fh.setLevel(logging.INFO)
#把handler对象绑定logger对象
logger.addHandler(ch)
logger.addHandler(fh)
#生成formatter对象
#把formatter对象绑定handler对象
file_format= logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_format = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(console_format)
fh.setFormatter(file_format)
logger.debug('test_log')
#console:DEBUG
#global:INFO(默认级别最高,如果不设置,就是说默认是Warning,就是说默认的就是Warning)
#file:Warning
  • 问题:如上述所示:全局级别的设置为:INFO,屏幕的为DEBUG,文件的为INFO,这时候直接结果为:
    文件有内容,屏幕不输出;
    2.如果把全局设置为DEBUG这时候看看:结果如何:
    会发现,屏幕的输出了,而文件的没有输出;
    3.如果全局的不设置会怎样,:
    结果发现,屏幕和文件都没有输出,因为全局的如果不写,默认是Warning,低于Warning都不会输出;