Python内置hash函数为什么运行两次值不一样

本文深入解析Python中字符串的Hash算法,揭示其如何通过添加secretprefix和secretsuffix增强安全性,防止hash表攻击。同时,文章探讨了CPython3.x中str对象的内部实现,包括unicodedata_db缓存和code_magic的使用,以及如何确保hash值在解释器进程内的一致性。
部署运行你感兴趣的模型镜像

解释一

python的字符串hash算法并不是直接遍历字符串每个字符去计算hash,而是会有一个secret prefix和一个secret suffix,可以认为相当于是给字符串加盐后做hash,可以规避一些规律输入的情况

显然这个secret前后缀的值会直接影响计算结果,而且它有一个启动时随机生成的机制,只不过,在2.x版本中,这个机制默认是关闭的,前后缀每次启动都设置为0,除非你改了相关环境变量来要求随机,而在3.x中修改了默认行为,如果你不配置环境变量,则默认是随机一个前后缀值,这样每次启动都会不同

这个环境变量是PYTHONHASHSEED,无论在2.x还是3.x中,配置为一个正整数,将作为随机种子;配置为0,则secret前后缀默认清零(和2.x默认行为就一样了),配置为空串或“random”,则表示让进程随机生成(和3.x默认行为一样)

具体为啥要这么做,猜测一个是为了安全性(防字符串hash表的攻击,比如php曾经碰到的攻击),另一个可能也是强调不要依赖一些内建结果,因为这种算法可能随着版本而更新,避免有些用户不看文档,误以为是永远不变的



作者:冒泡
链接:https://www.zhihu.com/question/57526436/answer/153241020
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

解释二

set/dict的hash还真就是这个玩意实现的,因为它保证了在同一个解释器进程里相同字符串hash一致。

因为CPython 3.x里的str,它的实体是unicode对象,实体是个utf-8 bytes或者是wstr(嗯这里真特么有个『或者』),并且通过一个叫做unicodedata_db的玩意来实现缓存(不然就没法儿保证str对象的不可变与地址一致性了)。

于是乎当你调内部hash的时候,反正不同进程中的解释器不会共用一个unicodedata_db,这个解释器进程里的字符串的hash到另一个进程里指不定连个字符串都不是,所以在计算这个内部hash的时候加入了一个code_magic的玩意,同时也均摊了一把复杂度,省得这个db以及set/dict对特定数据表现出极差性能。再说了,谁也不会傻到拿个解释器内部hash去做跨进程交换。

所以真需要做可重现可跨进程保持一致性的hash,请用hashlib。



作者:Coldwings
链接:https://www.zhihu.com/question/57526436/answer/153238652
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

您可能感兴趣的与本文相关的镜像

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

<think> 我们要求每次调用函数时,为传入的字典(键是序号,是类名)动态随机分配颜色。注意:类数量固定,所以我们需要一个能够适应任意数量类别的随机颜色生成方案。 思路: 1. 由于每次调用时类别数量固定,我们可以使用随机数生成RGB颜色(每个分量在0-255之间)。 2. 为了避免每次对同一个类名分配同的颜色(如果多次调用),我们可以考虑使用类名作为随机种子,这样相同的类名总是得到相同的颜色(但同类名随机)。但注意:用户要求动态随机,且没有提到需要一致性。根据问题描述,每次调用函数时动态随机分配即可,即同调用即使相同类名也可能同颜色?但通常目标检测可视化中,我们希望同一类别的对象颜色一致,所以这里我们考虑使用类名作为固定种子的依据,以保证同一类别在同一个映射中得到相同颜色,而同类别颜色同且随机。 然而,问题要求“动态随机映射”,并且类数量固定,所以有两种理解: a) 每次调用函数时,为传入的类名重新随机分配颜色(同一程序多次运行相同类名颜色可能同,但同一次调用中相同类名颜色相同)。 b) 或者,每次调用函数时,随机分配颜色,但相同类名在同一个映射中颜色相同。 由于目标检测可视化的需求,通常我们希望同一类别的对象颜色一致,因此我们采用类名作为颜色生成的依据(即同一个类名对应一个固定颜色,但同类名颜色随机)。但注意:如果两次调用传入的字典同,即使有相同的类名,我们也可以重新随机(或者也可以固定?)这里根据问题描述,没有要求跨调用的一致性,所以我们可以设计为: - 在同一个映射函数调用中,相同类名(即使序号同)会映射到相同颜色。 - 但问题中的字典是序号映射到类名,因此我们实际需要的是为每个类名分配颜色,而是序号。因为序号可能同但类名相同,那么我们希望相同类名的颜色相同。 但是,注意字典的键是序号,才是类名。同一个类名可能出现在多个序号上?(太可能,通常每个序号对应一个唯一的类名)所以这里我们假设每个序号对应一个唯一的类名,那么我们可以为每个序号(或每个类名)分配颜色。但问题要求是“将每一个序号对应于一个特定的颜色”,所以我们的映射函数应该是以序号为键,颜色为。 然而,如果我们希望相同类名(即使在同序号)使用相同颜色,那么我们就需要以类名为基准。但问题描述中并没有提到会有多个序号对应同一个类名的情况,所以这里我们按照序号来分配颜色,每个序号一个颜色。但是,如果两个序号对应的类名相同,那么它们会被分配两种同的颜色,这可能会让用户困惑。因此,更合理的做法是以类名为基准分配颜色。 重新理解需求:目标检测可视化中,我们通常希望同一类别的对象(即类名相同)使用相同颜色。因此,我们应该根据类名来分配颜色,而是序号。但是,传入的字典是序号到类名的映射,那么我们需要的是: - 对于相同的类名,分配相同的颜色。 - 同的类名,分配同的颜色(如果可能,但颜色数量有限,可以重复,但尽量同)。 然而,问题要求是“将每一个序号对应于一个特定的颜色”,但序号和类名是一一对应的吗?如果是,那么按序号分配和按类名分配效果一样。但如果是一一对应(多个序号对应同一个类名),那么按序号分配会导致同一个类名有多个颜色,这符合可视化需求。 因此,我们设计函数时应该考虑:为每个类名生成一个颜色,然后序号对应的颜色就是其类名对应的颜色。 步骤: 1. 遍历字典的(即类名),去重,得到所有类名。 2. 为每个类名生成一个随机颜色(使用类名的字符串作为种子,以保证同一个类名总是得到相同颜色,即使多次调用该函数(只要在同一个程序运行中)?但是,问题要求动态随机,每次调用都可能同,所以我们可以使用固定种子,而是用随机数,但这样会导致同一个类名每次调用得到同颜色。 权衡:目标检测可视化中,我们希望同一幅图像中相同类别的对象颜色相同,而同类别的颜色同。同时,我们可能希望同次运行程序时,同一类别的颜色保持一致(比如在视频中每一帧相同类别颜色相同),所以固定类名到颜色的映射是有意义的。 但是,用户要求“动态随机映射”,所以我们可以提供两种模式: - 固定模式:使用类名作为种子,这样每次运行程序相同的类名得到相同颜色。 - 完全随机模式:每次调用都重新为每个类名随机分配颜色(同一类名在本次调用中相同,但下次调用可能同)。 由于问题没有明确,我们按照最常用的方式:使用类名作为种子,这样既保证了同一个程序多次运行中相同类名颜色相同,又保证了同类名颜色随机且同。同时,由于使用了种子,同一个类名每次运行程序得到的颜色也是固定的(除非程序重新运行)。 但是,如果类名集合发生变化,那么同一类名分配的颜色会保持一致吗?是的,因为种子只依赖于类名字符串。 因此,我们设计函数如下: 函数:create_color_mapping(class_dict, seed=None) 输入: class_dict: 字典,键是序号,是类名(字符串) seed: 可选整数,用于控制随机数生成器的种子,如果提供,则使用类名字符串的哈希作为种子的一部分(这样同类名有同的种子) 输出: 一个字典,键是序号,是一个RGB元组(三个0-255的整数) 具体步骤: 1. 收集所有唯一的类名。 2. 为每个类名生成一个颜色: a. 为了确保相同类名总是得到相同颜色,我们使用类名的哈希作为随机种子(加上一个全局种子,如果提供了seed参数,则使用seed,否则使用0?)。注意:同一个类名在同的运行中,如果哈希相同,则种子相同,但Python运行中哈希可能同(除非设置PYTHONHASHSEED),所以我们可以用类名字符串的哈希(取绝对)模一个很大的数(比如2^31-1)作为种子,然后生成随机数。 另一种方法:使用随机,而是使用哈希函数生成一个颜色。例如,我们可以将类名字符串通过哈希函数映射到0到2^24-1之间的一个整数,然后把这个整数分解为RGB三个分量(每个分量0-255)。这样就需要随机数生成器了,而且可以保证相同类名得到相同颜色。 方法二更简单且确定,受随机种子影响,且跨运行一致。 步骤: color_int = hash(class_name) % (2**24) # 取哈希并模2^24,得到0到2^24-1之间的整数 r = (color_int >> 16) & 0xFF g = (color_int >> 8) & 0xFF b = color_int & 0xFF 但是,哈希冲突可能导致同类名得到相同颜色,但概率很小(2^24种颜色,类别数一般远小于这个数)。 然而,我们也可以使用HSV空间,将类名哈希映射到色相(H),然后固定饱和度和亮度,这样颜色会更鲜艳。 考虑到目标检测可视化,我们希望颜色鲜艳且易于区分,所以使用HSV模式生成颜色,然后转成RGB。 步骤: h = hash(class_name) % 360 # 色相取0-359度 s = 80% + 20%的随机性?但为了确定性,固定s和v 我们可以固定s=90%,v=90%,这样颜色鲜艳。 但是,如果类名很多,色相环可能会重复,但360个色相,如果类别超过360,则重复可避免。 因此,我们可以: h = hash(class_name) % 360 s = 90 + (hash(class_name) % 10) # 饱和度在90%-100%之间,增加一点变化 v = 90 + (hash(class_name) % 10) # 亮度在90%-100%之间 然后转换为RGB。 注意:同一个类名,多次计算哈希,在同一个Python运行中是固定的,但运行中,如果设置了PYTHONHASHSEED为固定,则相同,否则可能同。Python3.3以后,默认哈希随机化(每次运行同),所以如果我们希望跨运行相同,则需要使用一个非随机的哈希函数(如md5,然后取部分)或者使用一个固定的种子。 但是,用户要求“动态随机”,这里我们理解为在同一个运行中,每次调用函数时,相同的类名得到相同的颜色,同的类名得到同的颜色,但运行之间可以同(即要求跨运行一致)。所以我们可以直接使用Python内置hash函数。 然而,如果用户希望跨运行一致,我们可以提供seed参数,然后使用一个确定的哈希函数(如md5)来计算。 为了简单,我们采用直接使用内置hash函数运行一致),并且使用HSV转RGB。 实现一个函数将HSV转换为RGB(注意:OpenCV中H是0-180,S和V是0-255,但这里我们使用标准HSV:H:0-360, S:0-100, V:0-100,然后转换到RGB:0-255) 由于我们只需要生成颜色,我们可以使用colorsys库(标准库)中的hsv_to_rgb,它接受H:0-1, S:0-1, V:0-1,然后输出RGB在0-1之间,然后我们乘以255并取整。 步骤: import colorsys h = (hash(class_name) % 360) / 360.0 # 归一化到0-1 s = (90 + hash(class_name) % 10) / 100.0 # 归一化到0-1,注意这里我们用的是90-99之间的 v = (90 + hash(class_name) % 10) / 100.0 r, g, b = colorsys.hsv_to_rgb(h, s, v) 将r,g,b乘以255,然后取整,并转换为整数。 然后,对于传入的字典,我们遍历每个序号,找到对应的类名,然后得到颜色,存入新字典。 但是,如果两个同的类名哈希取模360后相同,且s和v的扰动也相同,那么颜色会相同。为了避免,我们可以考虑使用更多的信息(比如类名字符串本身)来生成s和v,但冲突概率已经很低。 另外,我们也可以使用RGB直接生成,但颜色可能够鲜艳。这里我们选择HSV方法。 考虑到可能引入colorsys(虽然它是标准库,但为了减少依赖,我们也可以自己写转换函数),但转换函数复杂,但为了简洁,我们使用colorsys。 因此,函数如下: 注意:由于同一个类名可能出现多次(对应多个序号),我们只需要计算一次颜色。 实现: 1. 创建一个空字典,用于存储类名到颜色的映射。 2. 遍历class_dict的(类名),如果这个类名还没有在映射中,则计算其颜色,并保存。 3. 然后创建一个新的字典,键为序号,为对应类名的颜色。 代码: 注意:hash可能是负数,所以取模前要取绝对?或者使用abs(hash(class_name)),但注意取模时负数取模会得到负数,所以我们可以: h = abs(hash(class_name)) % 360 但注意:Pythonhash函数对于相同的字符串在同一进程中是相同的,但同进程可能同(由于哈希随机化)。因此,我们使用内置hash函数。 但是,如果类名是中文等,hash函数也能工作。 另外,如果类名是None或其他可哈希类型,我们需要转换为字符串。 我们确保类名是字符串,如果是,则转换为字符串。 因此,最终函数: 由于问题要求每次调用函数时动态随机,但又要保证同一个类名在本次调用中颜色相同,所以符合我们的设计。 另外,我们也可以提供一种完全随机的模式(考虑类名相同),但符合可视化需求,所以采用。 编写函数: </think> 以下是一个动态随机颜色映射函数的实现,根据类名生成随机颜色,适用于类数量固定的目标检测可视化场景: ```python import random import hashlib def create_dynamic_color_mapping(class_dict): """ 为类别字典创建动态随机颜色映射 :param class_dict: 字典 {序号: 类名} :return: 颜色映射字典 {序号: (R, G, B)} """ color_map = {} class_colors = {} # 缓存类名到颜色的映射 for idx, class_name in class_dict.items(): # 如果类名已分配过颜色,则复用 if class_name in class_colors: color_map[idx] = class_colors[class_name] continue # 为新的类名生成随机但可重现的颜色 # 使用类名哈希作为随机种子,确保相同类名总是相同颜色 seed = int(hashlib.md5(class_name.encode()).hexdigest()[:8], 16) random.seed(seed) # 生成高饱和度、中等亮度的颜色(确保在可视化中明显) h = random.random() # 色相 (0-1) s = 0.7 + random.random() * 0.3 # 饱和度 (0.7-1.0) v = 0.6 + random.random() * 0.3 # 亮度 (0.6-0.9) # 将HSV转换为RGB # 公式参考: https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB h_i = int(h * 6) f = h * 6 - h_i p = v * (1 - s) q = v * (1 - f * s) t = v * (1 - (1 - f) * s) if h_i == 0: r, g, b = v, t, p elif h_i == 1: r, g, b = q, v, p elif h_i == 2: r, g, b = p, v, t elif h_i == 3: r, g, b = p, q, v elif h_i == 4: r, g, b = t, p, v else: r, g, b = v, p, q # 转换为0-255范围的整数 r, g, b = int(r * 255), int(g * 255), int(b * 255) # 缓存结果 color_map[idx] = (r, g, b) class_colors[class_name] = (r, g, b) return color_map ``` ### 使用示例 ```python # 类别字典(序号:类名) class_dict = { 0: "person", 1: "car", 2: "bicycle", 3: "dog", 4: "cat" } # 创建动态颜色映射 color_map = create_dynamic_color_mapping(class_dict) print(color_map) # 可能输出: {0: (214, 78, 96), 1: (88, 179, 102), 2: (155, 102, 204), ...} # 在目标检测可视化中使用 for detection in detections: class_id = detection["class_id"] class_name = class_dict[class_id] color = color_map[class_id] # 使用颜色绘制边界框(OpenCV示例) x1, y1, x2, y2 = detection["bbox"] cv2.rectangle(image, (x1, y1), (x2, y2), color, 2) cv2.putText(image, class_name, (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) ``` ### 关键特性 1. **动态适应**:自动处理任意数量的类别 2. **颜色一致性**: - 相同类名总是获得相同颜色(通过类名哈希保证) - 同类名获得同颜色(通过随机色相保证) 3. **视觉优化**: - 高饱和度确保颜色鲜艳易区分 - 中等亮度避免过暗或过亮 - HSV色彩空间提供更均衡的颜色分布 4. **高效缓存**: - 避免重复计算相同类名的颜色 - $O(n)$时间复杂度,$n$为类别数量 ### 技术说明 1. **颜色生成原理**: - 使用HSV色彩空间(色相、饱和度、亮度)代替RGB - 色相($H$)随机分布在0-360°范围: $H \sim U(0,1)$ - 饱和度($S$)控制在0.7-1.0: $S = 0.7 + 0.3U(0,1)$ - 亮度($V$)控制在0.6-0.9: $V = 0.6 + 0.3U(0,1)$ 2. **哈希稳定性**: ```python seed = int(hashlib.md5(class_name.encode()).hexdigest()[:8], 16) ``` 使用MD5哈希确保: - 相同类名 => 相同种子 => 相同颜色 - 同类名 => 同种子 => 同颜色 - 跨平台一致性(依赖Python内置哈希) 3. **避免颜色冲突**: - 当类别数量$N > 360$时可能发生色相冲突 - 实际检测场景中$N$通常小于100 - 可通过增加饱和度/亮度随机性进一步降低冲突概率[^1] 这种方法在YOLO等目标检测系统的可视化中广泛使用,能有效区分同类别[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值