Python进阶教程:18_hashlib 模块 零基础超详细教程 hashlib是 Python 内置的哈希散列计算标准库不需要额外安装可以快速生成文本、文件的「哈希摘要」也叫散列值、指纹。它是密码存储、文件完整性校验、数据去重、防篡改等功能的底层基础也是新手进阶必学的工具模块。本文从最基础的「哈希是什么」讲起全程配可运行代码 逐行解释 新手避坑指南零基础也能完全吃透。一、前置必懂什么是哈希1. 通俗理解哈希 数字指纹你可以把哈希算法理解成一台「指纹生成机」输入任意长度的数据一段文字、一个文件、一张图片输出固定长度的字符串比如 32 位、64 位就是这个数据的「哈希摘要 / 指纹」就像每个人的指纹都是唯一的只要原始数据有一点点改动生成的指纹就会完全不同相同的输入永远会得到相同的指纹。2. 哈希算法的四大核心特点这四个特点是所有哈希应用的基础新手一定要记牢单向不可逆只能从原始数据算出哈希值绝对不能从哈希值反推出原始数据和加密解密不一样加密是可逆的长度固定不管输入是 1 个字符还是 10GB 的文件同一种算法输出的哈希值长度永远一样比如 MD5 永远是 32 位十六进制字符串雪崩效应原始数据哪怕只改一个字、一个标点生成的哈希值都会天差地别完全看不出关联确定性相同的输入、相同的算法无论算多少次结果永远一模一样3. hashlib 模块能做什么Python 的hashlib封装了所有主流哈希算法帮我们快速计算哈希值常见用途文件完整性校验下载软件 / 电影后算 MD5 和官方对比确认文件没被篡改、没下载损坏密码安全存储数据库里不存明文密码只存密码的哈希值就算数据库泄露也拿不到原密码数据去重给文件 / 内容算哈希相同内容哈希一样通过哈希快速判断重复防篡改接口签名、数据校验确认数据传输过程中没被修改二、基础使用通用三步法所有哈希算法的使用流程完全一致记住三步即可创建哈希对象选择算法初始化一个计算对象喂入数据把要计算的二进制数据传进去可以分批喂获取结果生成最终的哈希摘要1. 第一个入门例子计算字符串的 MD5# 1. 导入内置模块无需安装 import hashlib # 2. 创建MD5算法的哈希对象 md5_obj hashlib.md5() # 3. 喂入数据必须传 bytes 字节类型字符串要先用 encode 转成字节 # 重点哈希是对二进制数据计算的字符串不能直接传 text Hello Python md5_obj.update(text.encode(utf-8)) # 4. 获取最终哈希摘要hexdigest() 返回十六进制字符串最常用 result md5_obj.hexdigest() print(f原始字符串{text}) print(fMD5 哈希值{result})运行结果原始字符串Hello Python MD5 哈希值a709c173228d32d242b7e38913a774a9逐行解释hashlib.md5()创建一个专门用来算 MD5 的计算对象类似准备好一台 MD5 指纹机.encode(utf-8)把字符串转成二进制字节这是新手 100% 会踩的坑 ——hashlib 所有 update 都只能传字节传字符串会直接报错.update(数据)把数据喂给哈希对象开始计算.hexdigest()输出最终的哈希值格式是十六进制字符串也是日常开发最常用的输出格式2. 两种输出摘要的方式表格方法返回类型特点适用场景.hexdigest()十六进制字符串可读性好由 0-9 和 a-f 组成绝大多数场景存数据库、打印对比.digest()bytes 字节串体积小不可读网络传输、二进制存储示例对比import hashlib md5_obj hashlib.md5(btest) # 也可以直接在创建时传初始数据 print(十六进制, md5_obj.hexdigest()) # 098f6bcd4621d373cade4e832627b4f6 print(字节格式, md5_obj.digest()) # b\t\x8fk\xcdF!\xd3s\xca\xdeN\x83\\xb4\xf63. 可以多次 update 分批喂数据哈希支持增量计算你可以分多次把数据喂进去最终结果和一次性喂完全一样。这个特性非常重要后面计算大文件会用到。import hashlib md5_obj hashlib.md5() # 分三次喂数据 md5_obj.update(bHello ) md5_obj.update(bPython) # 结果和一次性喂Hello Python完全一样 print(md5_obj.hexdigest()) # a709c173228d32d242b7e38913a774a9三、常用哈希算法 示例hashlib支持十几种算法新手掌握最常用的 3 类即可不同算法只是创建对象的函数名不一样使用流程完全相同。1. MD5 算法输出长度32 位十六进制字符串特点计算速度极快、体积小但安全性低已经被破解存在碰撞风险不同数据可能算出相同 MD5适用场景非安全场景的文件完整性校验、普通数据去重绝对不能用于密码、签名等安全场景import hashlib def get_md5(text: str) - str: 计算字符串的MD5值 md5 hashlib.md5(text.encode(utf-8)) return md5.hexdigest() print(get_md5(123456)) # e10adc3949ba59abbe56e057f20f883e2. SHA-1 算法输出长度40 位十六进制字符串特点比 MD5 稍慢安全性也已经被攻破不推荐用于安全场景适用场景旧系统兼容、普通文件校验import hashlib sha1 hashlib.sha1(bHello Python) print(SHA1值, sha1.hexdigest()) # 输出6b0d92ce83a04c7c40c57f9d9a3e0a3c2d7e3f0b3. SHA-2 系列主流安全选择这是目前最主流、最安全的通用哈希算法安全场景优先用这个系列最常用的是 SHA256。SHA256输出 64 位十六进制安全性高、速度适中绝大多数安全场景的首选SHA512输出 128 位十六进制安全性更高速度稍慢对安全要求极高的场景使用SHA256 示例import hashlib def get_sha256(text: str) - str: 计算字符串的SHA256值 sha256 hashlib.sha256(text.encode(utf-8)) return sha256.hexdigest() print(get_sha256(123456)) # 输出8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92算法对比总结表算法输出长度十六进制速度安全性推荐用途MD532 位极快低已破解普通文件校验、非安全去重SHA140 位快低已破解旧系统兼容、非安全校验SHA25664 位中等高接口签名、防篡改、安全校验SHA512128 位稍慢极高高安全级别的签名、校验 新手选型建议只是做文件完整性、去重追求速度 → 用 MD5涉及安全、防篡改、签名 → 直接用 SHA256不用纠结存密码 → 都不要用后面会讲原因和正确方案四、核心特性演示雪崩效应哈希最经典的特性原始数据哪怕只改一个字符哈希值都会完全不一样像雪崩一样。我们用代码直观验证一下import hashlib text1 今天天气真好 text2 今天天气真不好 # 只多了一个不字 md5_1 hashlib.md5(text1.encode()).hexdigest() md5_2 hashlib.md5(text2.encode()).hexdigest() print(f原文1{text1}) print(fMD5{md5_1}) print(-*50) print(f原文2{text2}) print(fMD5{md5_2})运行结果原文1今天天气真好 MD5a1b2c3...示例 -------------------------------------------------- 原文2今天天气真不好 MD5x9y8z7...示例可以看到只差了一个字两个 MD5 没有任何相似之处完全无法通过哈希值猜测原文的差异。这也是哈希能用来判断数据是否被篡改的核心原理。五、进阶实战计算大文件的哈希计算小字符串可以一次性 encode但如果是几个 GB 的大文件一次性读进内存会直接导致内存溢出。这时候就要用到「分块读取 多次 update」的方式边读边算内存占用极低。完整示例计算文件的 MD5import hashlib def get_file_md5(file_path: str) - str: 计算大文件的MD5值分块读取不占内存 :param file_path: 文件路径 :return: MD5十六进制字符串 md5_obj hashlib.md5() # 以二进制只读模式打开文件 with open(file_path, rb) as f: # 每次读取 4KB 数据循环读完整个文件 while True: chunk f.read(4096) # 4096字节 4KB # 读完了就退出循环 if not chunk: break # 分批喂入哈希对象 md5_obj.update(chunk) return md5_obj.hexdigest() # 使用示例计算当前目录下 test.txt 的MD5 if __name__ __main__: md5 get_file_md5(test.txt) print(f文件MD5{md5})代码解释open(file_path, rb)必须用二进制模式rb打开文件读出来的直接是字节不需要再转码f.read(4096)每次只读 4KB无论文件多大内存永远只占 4KB非常高效循环读完所有块每次都update最终结果和一次性读完整文件计算完全一致 拓展想算 SHA256 只需要把hashlib.md5()改成hashlib.sha256()其他代码完全不用动。六、经典应用场景实战场景 1文件完整性校验下载文件后对比官方给出的哈希值确认文件没有损坏、没有被植入病毒。import hashlib # 官方给出的正确MD5 official_md5 a1b2c3d4... # 计算你下载的文件的MD5 your_file_md5 get_file_md5(下载的安装包.exe) if your_file_md5 official_md5: print(文件完整没有被篡改) else: print(文件可能损坏或被篡改请重新下载)场景 2密码加盐哈希原理演示很多新手会直接把密码的 MD5 存进数据库这是非常危险的问题简单密码的 MD5 可以通过「彩虹表」反查出来比如123456的 MD5 全网都能查到解决方法加盐salt—— 给密码拼接一段随机字符串再算哈希大幅提升破解难度加盐哈希示例import hashlib import uuid def hash_password(password: str, salt: str None) - tuple[str, str]: 给密码加盐生成哈希 :param password: 原始密码 :param salt: 盐不传就自动生成随机盐 :return: (哈希值, 盐) # 生成随机盐用uuid生成唯一随机字符串 if salt is None: salt uuid.uuid4().hex # 32位随机十六进制字符串 # 密码 盐 拼接后算SHA256 sha256 hashlib.sha256((password salt).encode(utf-8)) password_hash sha256.hexdigest() return password_hash, salt # 注册时生成哈希和盐一起存进数据库 pwd 123456 pwd_hash, salt hash_password(pwd) print(f存储到数据库的哈希{pwd_hash}) print(f存储到数据库的盐{salt}) # 登录时用同样的盐计算对比结果 def verify_password(input_pwd: str, saved_hash: str, saved_salt: str) - bool: 验证密码是否正确 # 用用户输入的密码 存好的盐重新算哈希 calc_hash, _ hash_password(input_pwd, saved_salt) # 和数据库里存的哈希对比 return calc_hash saved_hash # 测试输入正确密码 print(verify_password(123456, pwd_hash, salt)) # True # 测试输入错误密码 print(verify_password(1234567, pwd_hash, salt)) # False⚠️ 非常重要的新手提醒 上面只是原理演示真实生产环境不要自己用 hashlib 写密码加密。 因为 MD5、SHA256 都是快哈希算法计算速度太快黑客可以每秒试几十亿次密码暴力破解成本很低。 真正存密码请用专门的「慢哈希库」比如bcrypt、argon2-cffi它们会故意放慢计算速度大幅提升暴力破解成本。场景 3批量文件去重给每个文件算哈希相同文件哈希一定一样通过哈希快速找出重复文件import os import hashlib def find_duplicate_files(folder_path: str) - dict: 找出文件夹里的重复文件 hash_map {} # key: 文件哈希, value: 文件路径列表 for root, _, files in os.walk(folder_path): for file in files: file_path os.path.join(root, file) file_md5 get_file_md5(file_path) if file_md5 in hash_map: hash_map[file_md5].append(file_path) else: hash_map[file_md5] [file_path] # 只返回有重复的 return {k: v for k, v in hash_map.items() if len(v) 1}七、新手高频易错点总结1. 直接传字符串报错update()只能接收bytes字节类型传字符串会报TypeError: Unicode-objects must be encoded before hashing。 ✅ 解决字符串用.encode(utf-8)转成字节再传。2. 以为哈希可以解密哈希是单向的只能正向计算不能反向解密。网上所谓的「MD5 解密」都是彩虹表查询本质是提前存好海量密码和对应的 MD5碰运气匹配不是真的解密。3. 用 MD5 存密码MD5 速度太快容易被暴力破解哪怕加了盐也不推荐用于密码存储。生产环境请使用专门的密码哈希库。4. 大文件一次性读取计算大文件哈希时不要一次性读进内存一定要分块循环读取避免内存溢出。5. 混淆十六进制和字节摘要日常打印、存数据库、对比都用hexdigest()的字符串格式digest()只用于特殊的二进制传输场景。