zoukankan      html  css  js  c++  java
  • python学习笔记-模块和包

    模块导入方法

    1、import 语句

    import module1[,module2[,...moduleN]]

    当我们使用import语句的时候,Python解释器是怎么找到对应对文件对呢?
    答案是解释器有自己的搜索路径,存在sys.path里
    2、form ...import 语句

    from modname import name1[,name2,[,...nameN]]

    3、from...import *

    from...import *

    注意:

    使用impor会执行下面的动作

    1、执行对应文件

    2、引入变量名

    引用多个模块时,可以用逗号隔开

    例子:

    def add(x,y):
        return x+y
    
    def sub(x,y):
        return x-y
    cal.py

    引入该模块有下面的写法

    import cal
    from cal import add
    from cal import sub
    from cal import *    #引入所有,不推荐

    例子:

    import cal
    
    def run():
        print(cal.add(3,7))
    main.py
    from my_lenson import main
    
    main.run()
    bin.py

    结果:

    执行bin.py文件报错,找不到模块名cal。main.py中的模块引用应改成 from  my_lenson import cal
    另一种方式是把路径添加到sys.path列表中。

    引入的路径只认执行文件的路径。执行bin文件程序会把..lenson添加到sys.path列表中。能通过头部模块引用找到my_lenson下的main文件,但是main文件中代码import cal只能在路径..lenson中找所以找不到。

    #补充
    import sys
    sys.path.append() 只是做临时修改,永久修改在系统的环境变量里操作。

    例子:

    如果目录结构为webweb1web2cal.py,要调用cal中的cal函数,写法

    from web.web1.web2 import cal
    print(cal.add(2,5))
    
    from web.web1.web2.cal import add
    print(add(2,6))
    
    from web.web1 import web2   #引用都会执行web,web1,web2的__init__文件,唯一不支持的调用方式
    print(web2.cal.add(2,5))    #报错

    补充:

    print(__name__)在执行文件的执行结果是__main__;在调用文件的执行结果是显示文件名。

    if __name__=="__main__":#调试的时候可以使用:执行的时候会执行,引用的时候不会执行

    功能1:用于被调用函数的测试

    功能2:不让其他人随意执行自己的程序

    如果我们是直接执行某个.py文件的时候,该文件中"__name__=='__main__'"是true,但是我们如果从另一个.py文件通过import导入该文件对时候,这时__name__的值是这个py文件名字而不是__main__。结果是false所以不会运行下面的代码。

    调试代码的时候,在"if __name__=='__main__'"中加入一些我们的调试代码,我们可以让外部模块调用的时候不执行我们的调试代码,但是如果我们想排查问题的时候,直接执行该模块文件,调试代码能够正常执行

    补充:

    执行bin,调用my_lenson下的main

     有这三种写法:

    import sys,os
    #方式一
    sys.path.append(r"D:PycharmProjects	est1lenson")  #写死的。不推荐
    
    #方式二
    BASE_DIR=os.path.dirname(os.path.dirname(__file__))#pycharm可以,在终端下执行不行.__file__只能得到文件名
    sys.path.append(BASE_DIR)
    
    #方式三
    BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.append(BASE_DIR)
    
    from my_lenson import main
    main.run()

     time时间模块

    1、time模块时间的格式转化

     

    import time
    #时间戳
    print(time.time())   #1970年1月1日。到现在经过的秒数
    print(time.localtime()) #结构化时间对象,当地时间
    #默认相当于localtime(time.time())
    t=time.localtime()
    print(t.tm_year)
    print(t.tm_wday) #一周的第几天
    
    print(time.gmtime()) #标准时间UTC,世界统一时间。
    
    #=====时间格式的转化=====
    #结构化时间转化成时间戳
    print(time.mktime(time.localtime()))
    
    #将结构化时间转化成字符串时间
    print(time.strftime("%Y-%m-%d %X",time.localtime())) #%X表示时分秒
    
    #将字符串时间转化成结构化时间
    print(time.strptime("2016:12:24:15:50:36","%Y:%m:%d:%X"))
    
    #=========将时间格式转化成一种一种固定好的格式===========
    #将结构化时间转化成一种固定好的时间格式
    #默认是转化的当前时间
    print(time.asctime()) #显示格式为:Wed Feb 12 18:24:02 2020
    #将时间戳转化成一种固定好的时间格式
    print(time.ctime()) #显示格式为:Wed Feb 12 18:24:02 2020

    2、其他方法

    sleep():

    clock() #不常用,在win系统中指时间差

    *time.clock has been deprecated in Python 3.3 and will be removed from Python 3.8: use time.perf_counter or time.process_time instead

    3、datetime模块

    import datetime
    print(datetime.datetime.now())#2020-02-12 18:32:05.267959

    random模块

    import random
    ret=random.random() #(0,1)--float
    print(ret)
    
    print(random.randint(1,3)) #[1,2,3]
    print(random.randrange(1,3))#[1,2),不含3
    print(random.choice([1,'23',[4,5]]))#从列表里随机取值
    print(random.sample([11,22,33,44],2))#随机选2个,结果是列表
    print(random.uniform(1,4)) #1到4的浮点数
    
    ret=[1,2,3,4,5]
    random.shuffle(ret)
    print(ret) #把顺序打乱的一个列表 [1, 3, 5, 2, 4]

    例子:生成一个随机验证码

    def v_code():
        ret=''
        for i in range(5):
            num=random.randint(0,9)
            alf=chr(random.randint(65,122))
            s=str(random.choice([num,alf]))
            ret+=s
        return ret
    print(v_code())

    os模块

    import os
    print(os.getcwd()) #获取当前工作目录,即当前python脚本工作的目录路径 os.chdir("lenson")# 改变当前脚本工作目录,相当于shell下cd print(os.curdir) #返回当前目录:("."),不常用 print(os.pardir) #获取当前目录的父目录字符串名:("..") ,不常用 os.makedirs("dirname1/dirname2") #可生成多层递归目录 os.removedirs(r"D:PycharmProjects est1lensondirname1dirname2") #若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依次类推 os.mkdir("dirname") #生成单级目录;相当于shell中mkdir dirname os.rmdir(r"D:PycharmProjects est1lensondirname1dirname2") #删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname print(os.listdir(r"D:PycharmProjects est1lenson")) #列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印 os.remove() #删除一个文件 os.rename("oldname","newname") #重命名文件/目录 print(os.stat(r"D:PycharmProjects est1lensonin.py"))#获取文件/目录信息,大小,修改时间等 os.sep #输出操作系统特定的路径分割符,win下为"\",linux下为"/" print(os.linesep) #输出当前平台的行终止符,win 下为" ",linux下为" " print(os.pathsep) #输出用于分割文件路径的字符串 win下为;,linux下为: print(os.name) #输出字符串指示当前使用平台。win->"nt";linux->"posix" ,不常用 os.system("bash command") #运行shell命令,直接显示,不常用 print(os.environ)# 获取系统环境变量,不常用 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.exist(path) #如果path存在,返回true;不存在返回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[,...]]) #将多个路径组合后返回,第一个绝对路径之前的参数将被忽略 os.path.getatime(path) #返回path所指向的文件或者目录的最后存取时间 os.path.getmtime(path) #返回path所指向的文件或者目录的最后修改时间

    sys模块

    sys.argv  命令行参数list,第一个元素是程序本身路径
    #sys.argv[1],sys.argv[2]。显示传的参数。比如上传下载的时候可以使用。
    sys.exit(n) 退出程序,正常退出时exit(0)
    sys.version 获取python解释程序的版本信息
    sys.maxint() 最大Int值
    sys.path    返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
    sys.platform 返回操作系统平台名称

    例子:进度条显示

    import time
    for i in range(100):
        sys.stdout.write("#") #向屏幕显示对应的内容。print是以此为基础的。
        time.sleep(0.2)
        sys.stdout.flush()   #程序通过sys的接口把要输出的内容放在了缓存里,最后才显示到终端,所以加个刷新。

    json&pickle模块

    之前讲用eval内置方法可以将一个字符串转成python对象,不过,eval方法是有局限性的,对于普通的数据类型,json.loads和eval都能用,但遇到特殊类型的时候,eval就不管用了,所以eval的重点还是通常用来执行一个字符串表达式,并返回表达式的值。

    如:

    import json
    x="[null,true,false,1]"
    print(eval(x)) #报错
    print(json.loads(x))

    方法:

    json.dumps()   转化成字符串,封装成json格式的字符串
    json.loads()

    什么是序列化?

    我们把对象(变量)从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling,在其他语言中也被称之为serialization,marshalling,flattening等等,都是一个意思。

    序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。

    反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling。

    json

    如果我们要在不同的编程语言之间传递对象,就必须把对象序列化为标准格式,比如XML,但更好的方法是序列化为JSON,因为JSON表示出来就是一个字符串,可以被所有语言读取,也可以方便地存储到磁盘或者通过网络传输。JSON不仅是标准格式,并且比XML更快,而且可以直接在Web页面中读取,非常方便。

    JSON表示的对象就是标准的JavaScript语言的对象,JSON和Python内置的数据类型对应如下

    import json
    '''
    dic={'name':'jobs'} #-->{"name":"jobs"}-->'{"name":"jobs"}'
    i=8                 #-->'8'
    s='hello'           #-->"hello"-->'"hello"'
    l=[11,22]           #-->"[11,22]"
    '''
    #------序列化-----
    dic={'name':'jobs'}
    f=open("序列化对象","w")
    dic_str=json.dumps(dic)  #转化成json格式的字符串,如上的形式,单引号会变成双引号
    f.write(dic_str)       #----等价于json.dump(dic,f)
    f.close()
    
    #------反序列化-----
    import json
    f=open("序列化对象")
    data=json.loads(f.read()) #---等价于data=json.load(f)
    注意:无论数据是怎样创建的,只要满足json格式,就可以json.loads出来,不一定非要dumps的数据才能loads
    import json
    # dct="{'a':'111'}"     #报错,json不认单引号
    # dct=str({"a":"111"})   #报错,因为生成的数据还是单引号
    
    dct='{"a":"111"}'
    print(json.loads(dct))

    pickle

    #pickle与json在使用上是完全一致的。pickle在序列化处理成字节bytes。json是处理成字符串str
    #pickle支持的数据类型更多。类,函数。但是用处需求少很少用。

    如:

    import pickle
    
    #------序列化-----
    dic={'name':'jobs'}
    f=open("序列化对象","wb")    #注意是w是写入str,wb是写入bytes
    dic_p=pickle.dumps(dic)    #dic_p是'bytes'
    f.write(dic_str)           #----等价于pickle.dump(dic,f)
    f.close()
    
    #------反序列化-----
    import pickle
    f=open("序列化对象","rb")
    data=pickle.loads(f.read()) #---等价于data=pickle.load(f)

    shelve模块  

    shelve模块比pickle模块简单,只有一个open函数,返回类似字典的对象,可读可写;key必须为字符串,而值可以是python所支持的数据类型

    import shelve
    f=shelve.open(r'shelve') #目的:将一个字典放入文本
    f['stu1_info']={'name':'steven','age':'18'}
    f['stu2_info']={'name':'job','age':'20'}
    f.close()
    
    print(f.get('stu1_info')['age']) #反序列化

    xml模块

    xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单,不过,古时候,在json还没诞生的黑暗年代,大家只能选择用xml呀,至今很多传统公司如金融行业的很多系统的接口还主要是xml。

    xml的格式如下,就是通过<>节点来区别数据结构的:

    import xml.etree.ElementTree as ET
    
    tree=ET.parse(r"C:UsersAdministratorDesktop	est.xml")
    root=tree.getroot()
    print(root.tag)
    #----遍历-----
    for child in root:
        print(child.tag,child.attrib)
        for i in child:
            print(i.tag,i.text)
    #----只遍历某个节点----
    for node in root.iter('year'):
        print(node.tag,node.attrib)
    #----修改----
    for node in root.iter('year'):
        new_year=int(node.text)+1
        node.text=str(new_year)
        node.set("updated","yes")
    tree.write("abc.xml")
    #-----删除----
    for country in root.findall("country"):
        rank=int(country.find("rank").text)
        if rank > 50:
            root.remove(country)
    tree.write("output.xml")
    #-----创建-----
    import xml.etree.ElementTree as ET
    new_xml=ET.Element("data")
    country=ET.SubElement(new_xml,"country",attrib={"name":"American"})
    rank=ET.SubElement(country,"rank")
    rank.text='2'
    year=ET.SubElement(country,"year",attrib={"checked":"no"})
    year.text='200'
    country=ET.SubElement(new_xml,"country",attrib={"name":"china"})
    rank=ET.SubElement(country,"rank")
    rank.text='2'
    year=ET.SubElement(country,"year",attrib={"checked":"yes"})
    year.text='5200'
    et=ET.ElementTree(new_xml) #生成文档对象
    et.write("test.xml",encoding="utf-8",xml_declaration=True)
    ET.dump(new_xml)

    re模块

    本质而言,正则表达式(或 RE)是一种小型的、高度专业化的编程语言,(在Python中)它内嵌在Python中,并通过 re 模块实现。正则表达式模式被编译成一系列的字节码,然后由用 C 编写的匹配引擎执行。

    字符匹配(普通字符,元字符):

    1 普通字符:大多数字符和字母都会和自身匹配
                  >>> re.findall('jobs','yuanaleSxjobszhoumo')
                          ['jobs'] 

    2 元字符:. ^ $ * + ? { } [ ] | ( )

    import re
    #.:通配符,什么都能匹配,除了换行符
    re.findall("a..x","adsfaeexkksdjfis")
    #^:表示以什么开头
    re.findall("^a..x","adsxfajdkfjkadfjka") #匹配以a开头。。。
    #$:表示以什么结尾
    re.findall("a..x$","adfakfjakfjasix")#匹配结尾是x
    #*+{}? 可以匹配重复
    #*:表示0到无穷次
    #+:1到无穷次
    re.findall("^d*","ddddfajdfkajdfk")
    re.findall("seex*","asdkfafdaseexxx")
    re.findall("seex+","asdkfafdaseexxx")
    re.findall("seex*","asdkfafdseele")#0次能匹配上  ['see']
    re.findall("seex+","asdkfafdseele")#不能匹配上
    #?:表示0或1次
    
    #{};表示自己定义次数
    '''
    {0,} == *
    {1,} == +
    {0,1}== ?
    {6}   表示匹配重复6次
    {1,6} 表示重复1到6次
    '''

    注意:

    前面的*,+,?等都是贪婪匹配,也就是尽可能匹配,后面加?使其变成惰性匹配
    ret=re.findall("abc*?","abcccc")
    print(ret)#["ab"]

    3 字符集[]

    #[]:字符集 - ^ 这三个字符有特殊意义,^在里面表示非。
    re.findall("a[bc]","abccc")  #['ab']
    re.findall("q[a-z]","akjdkfqba")#["qb"]  中括号里只有-有特殊意义。 表示a-z任一字符
    re.findall("q[a*z]","adkfjakqadkq*kk")#["qa",q*] 这里的*没有特殊意思。
    re.findall("q[a-z]*","qdfjakdjka")#["qdfjakdjka"]
    re.findall("q[a-z]*","qdfjakdjka877")#["qdfjakdjka"]
    re.findall("q[0-9]*","q38423fkafdkfjq")#["q38423","q"]
    re.findall("q[^a-z]","q38djffjk")#["q38"]

    4 元字符之转义符

    反斜杠后边跟元字符去除特殊功能,比如.
    反斜杠后边跟普通字符实现特殊功能,比如d

    d 匹配任何十进制数;相当于类[0-9]
    D 匹配任何非数字字符;相当于类[^0-9]
    s 匹配任何空白字符;相当于类[ fv]
    S 匹配任何非空白字符;相当于类[^ fv]
    w 匹配任何字母数字字符;相当于类[a-zA-Z0-9_]
    W 匹配任何非字母数字字符;相当于类[^a-zA-Z0-9_]
     匹配一个特殊字符边界,比如空格,&,#等

    print(re.findall("I","hello I am LIST")) #匹配不上,因为在ASCII表中是有意义的
    print(re.findall(r"I","hello I am LIST"))#加r表示字符串不做转义 ['I']
    print(re.findall("I\b","hello I am LIST"))#['I']
    print(re.findall("c\\l","abclerwt")) #['c\l']

     5元字符()分组

    import re
    re.findall(r"(ab)+","abccc")               #['ab'],未命名的分组
    re.search("(?P<name>w+)","abcccc")        #获得一个匹配的对象,命名的分组
    re.search("(?P<name>w+)","abcccc").group()#'abcccc'
    #?P是固定的,这个方式可以将匹配的结果赋给一个变量。用于在匹配的结果较多时获取想要的特定值,如下
    re.search("(?P<name>[a-z]+)(?P<age>d+)","steven33jobs22smile45").group("name")#'steven'
    re.search("(?P<name>[a-z]+)(?P<age>d+)","steven33jobs22smile45").group("age")#'33'

    注意
    #有分组时,匹配的结果会优先返回分组里匹配的内容

    re.findall("www.(baidu|hao123).com","www.baidu.com")#['baidu']

    #想要匹配的内容,用?:取消权限即可

    re.findall("www.(?:baidu|hao123).com","www.baidu.com")#['www.baidu.com']

    6元字符|或

    ret=re.search('(ab)|d','rabhdg8sd')
    print(ret.group())#ab

    7、re模块常用方法

    import re
    
    #1.findall
    re.findall('a','alvin yuan')           #返回所有满足匹配条件的结果,放在列表里
    #2.search
    re.search('a','alvin yuan').group()    #函数会在字符串内查找模式匹配,只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象可以
                                           #通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None。
    #3.match
    re.match("a","abc").group()            #同search类似,但是是在字符串开头处进行匹配
    #4.split
    re.split(" ","hello abc def")
    re.split("[ |]","hello abc|def")#['hello', 'abc', 'def']
    re.split("[ab]","abc")          #['', '', 'c'] 先按a分得到""和"bc",再对""和"bc"按b分割
    re.split("[ab]","asdabcd")      #['', 'sd', '', 'cd']
    #5.sub 替换
    re.sub("d+","A","dafj12dkf345kj88")#'dafjAdkfAkjA' 将匹配的结果用第二个参数替换
    re.sub("d+","A","dafj12dkf345kj88",2)#'dafjAdkfAkj88' 第四个参数为最多匹配替换的次数
    re.subn("d+","A","dafj12dkf345kj88")#('dafjAdkfAkjA', 3) 获取替换后的结果和次数
    #6.compile
    com=re.compile("d+")
    com.findall("qwe232iieu2123iii666")#['232', '2123', '666']
    #7.finditer 把结果放在迭代器里面
    ret=re.finditer("d","dadf873kiiue887")
    print(ret)#<callable_iterator object at 0x0399D0B0>
    print(next(ret).group())#8
    print(next(ret).group())#7

    logging模块

    一、简单应用

    import logging
    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 > NOTSET),
    默认的日志格式为:日志级别:Logger名称:用户输出消息。

    二、灵活配置

    import logging
    
    logging.basicConfig(
        level=logging.DEBUG,#配置日志的级别
        filename="logger.log", #输出到制定文件中
        filemode="w",#写文件的方式
        format="%(asctime)s %(filename)s[%(lineno)d] %(message)s"
        )
    
    #logger对象
    logger=logging.getLogger()
    
    fh=logging.FileHandler("test_log")    #配置输出到文件句柄
    ch=logging.StreamHandler()            #配置输出到控制台
    
    fm=logging.Formatter("%(asctime)s %(message)s")
    
    fh.setFormatter(fm)
    ch.setFormatter(fm)
    
    logger.addHandler(fh)
    logger.addHandler(ch)
    logger.setLevel("DEBUG")
    
    logger.debug("debug")
    logger.info("hello")
    logger.warning("warning")
    logger.error("error")
    logger.critical("critical")
    注意1:如果用logger1=logging.getLogger("mylogger")创建。相当于创建了一个root下的子用户mylogger。并且是唯一的。
    再创建一个同名的用户指向的是同一个对象。logger1=logging.getLogger("mylogger.sontree")创建子用户的子用户
    注意2:logger=logging.getLogger();logger1=logging.getLogger("mylogger")
    子对象和父对象都存在并且都在输出时。子对象会多输出一次。每向上多一个层级多输出一次。

    configparser模块

    配置解析

    import configparser
    
    config=configparser.ConfigParser() #config={}
    
    config["DEFAULT"]={'ServerAliveInterval':"45",
                       "Compression":"yes",
                       "CompressionLevel":"9"
        }
    
    config["bitbucket.org"]={}
    config["bitbucket.org"]["User"]="hg"
    
    config["topsecret.server.com"]={}
    topsecret=config["topsecret.server.com"]
    topsecret["Host Port"]="50022"
    topsecret["ForwardX11"]="no"
    
    config["DEFAULT"]["ForwardX11"]="yes"
    
    with open("example.ini","w") as configfile:
        config.write(configfile)

    结果:

    [DEFAULT]
    serveraliveinterval = 45
    compression = yes
    compressionlevel = 9
    forwardx11 = yes
    
    [bitbucket.org]
    user = hg
    
    [topsecret.server.com]
    host port = 50022
    forwardx11 = no
    example.ini

    增删改查

    #----查询----
    import configparser
    
    config=configparser.ConfigParser()
    config.read("example.ini")
    
    print(config.sections())#['bitbucket.org', 'topsecret.server.com']
    print('bitbucket.org' in config)#True
    print(config["bitbucket.org"]["User"])#hg
    for key in config["topsecret.server.com"]:#遍历下面的键,结果会包含了默认[DEFAULT]下面的键。 print(key)
    print(config.options("bitbucket.org"))#['user', 'serveraliveinterval', 'compression', 'compressionlevel', 'forwardx11'].显示所有的键同上,但是以列表的形式 print(config.items("bitbucket.org"))#[('serveraliveinterval', '45'), ('compression', 'yes'), ('compressionlevel', '9'), ('forwardx11', 'yes'), ('user', 'hg')] #把键和值组成元祖的形式显示 print(config.get("bitbucket.org","compression"))#yes #----删改增---- config.add_section("yuan") #增加一个块 config.set("yuan","k1","11111")#增加块下面的一个键值对 config.remove_section("yuan") #删除一个块 config.remove_option("yuan","k1")#删除块下面的键值对 config.write(open("example.ini","w"))

    hashlib 模块

    用于加密相关的操作,3.x里代替了md5模块和sha模块,主要提供SHA1,SHA224,SHA256,SHA384,SHA512,MD5算法
    import hashlib
    
    obj=hashlib.md5()#可以用其他加密算法等 obj=hashlib.sha256()
    obj.update("hello".encode("utf8"))
    print(obj.hexdigest()) #5d41402abc4b2a76b9719d911017c592 把不定长的字符串转换成定长的秘文
    
    obj.update("admin".encode("utf8"))#只执行admin的加密,结果为:21232f297a57a5a743894a0e4a801fc3
    print(obj.hexdigest()) #执行上面"hello"加密和"admin"加密,相当于对"helloadmin加密",结果为:dbba06b11d94596b7169d83fed72e61b
    以上加密算法虽然依然非常厉害,但也有存在缺陷,即:通过撞库可以反解。所以,有必要对加密算法中添加自定义key再来做加密
    import hashlib
    hash=hashlib.sha256("23192idfaljfie82".encode("utf8"))
    hash.update("alvin".encode("utf8"))
    print(hash.hexdigest())#ea1936a49f75518528fe0f2a60a412b8ff89360bcfe882ff4290bac45fbd26fa
    python还有一个hmac模块,它内部对我们创建key和内容再进行处理然后再加密:
    import hmac
    h=hmac.new("alvin".encode("utf8"))
    h.update("hello".encode("utf8"))
    print(h.hexdigest())#320df9832eab4c038b6c1d7ed73a5940
  • 相关阅读:
    不用π求坐标夹角大小
    使用LVS实现负载均衡原理及安装配置详解
    从dfs向动态规划过渡
    关于dfs
    [LeetCode] Add Two Numbers
    [LeetCode] Gray Code
    [LeetCode] Single Number
    第四章 深入JSP技术
    蚂蚁破2万亿!身价暴涨2077亿的彭蕾:无论马云的决定是什么,我都让它成为最正确的决定...
    异常场景测试
  • 原文地址:https://www.cnblogs.com/steven223-z/p/12372832.html
Copyright © 2011-2022 走看看