Python 中的哈希表:对字典的理解

  有没有想过,Python中的字典为什么这么高效稳定。原因是他是建立在hash表上。了解Python中的hash表有助于更好的理解Python,因为Python中字典无处不在。

hash 函数


  哈希函数是一个可以将任意长度的数据块映射到固定长度的值,这个步骤称为hash,也就是散列。

hash 函数有三个主要的特征:

  1. 计算迅速:计算一个数据块的hash值非常快
  2. 确定性:相同用字符串会产生相同的hash值
  3. 结果固定长度:不管输入的是,一个字节还是十个字节,或者上万个字节,结果总是预先确定的长度。

  另一个特征在hash函数中非常普遍,即他们是单方向的:通过函数实现后,原始数据丢失了,我们可以通过字符串得到一个hash值,但不能通过一个hash也就是散列得到原始的字符串(因为有对数据降维的方法会造成数据的丢失)。这种特性并不是对所有hash函数的强制性规定,但是当需要加密安全时,这种性质还是挺好用的。

一些比较受欢迎的算法包括:MD5、SHA-1、SHA-2,NTLM.

关于hash的一些使用方法


  很多东西都依赖hash,hash表就是其中一例,另一些用法是出于加密和的原因

   一个具体的例子就是当你尝试从网上下载开源软件时。通常你都会发现一个关联文件,这个关联文件就是这个文件签名。这个签名仅仅是源文件的散列值它非常有用,你为你可以自己通过计算下载好的文件的散列值并与网站上提供的签名进行对比,这样就可以确认你自己下载的文件没有损坏。

  另一种用法是存储用户的密码。你有没有问过你自己,当你忘掉了一个网站的登录密码的时候,你还想从这个网站上恢复登录,它只会让你重新确定一个新的登录密码而不是给你原来你选择的密码,这是因为网站并不会储存你完整的密码,而是你密码的hash值。

  这么做是出于安全因素,因为如果黑客搞到了数据库的权限,他们不会知道你真实的密码,而是仅仅得到你密码的哈希值,又因为哈希函数是单向的,他们根本不可能从哈希值得到你的密码

Python 里的hash()函数


  Python 中含有内置的函数去给对象生成哈希值,也就是hash()函数,这个函数将一个对象作为输入,返回一个整数的hash值。

  内部的,这个函数涉及到.\__hash\__()这个输入对象的方法,所以呢,如果你想使你的自定义类可哈希化,你需要做的是实现.\__hash__()这个方法返回出一个整数,这个整数是基于你的对象的内部状态决定的。

先开始一些基本的小例子,先对数值进行散列

>>> hash(1)
1
>>> hash(10)
10
>>> hash(10.00)
10
>>> hash(10.01)
230584300921368586
>>> hash(-10.01)
-230584300921368586

  如果你好奇为什么这些hash值看起来有不同的长度,请记住在Python中hash()函数返回的是整数对象,他们都会在标准的64位Python解释器中通过24字节呈现。

  正如你可以看到的,整数的哈希值默认的就是它本身,不管你哈希的数据类型是什么,所以整数1和浮点数1.0哈希值都是1.

  这个有什么特殊的么,这个展示了你之前学到的知识,也就是hash函数经常是单向的函数,如果两个不同的对象具有相同的hash值,根本不可能做反向工程,也就是从hash值返回到原始的对象,这也就使得被hash的原始数据类型的信息被丢失。

  另几个有趣的可以关注的hash事情是,小数的散列值不同于它本身,负数具有负的哈希值。还有一点,就是如果两个对象具有相同的哈希值,称为哈希碰撞。

哈希一个字符串不同于对数值进行哈希。

>>> hash("Bad Behaviour")
7164800052134507161

  DOS攻击(DOS代表拒绝服务)是指攻击者故意耗尽计算机系统的资源,使系统不再能够向客户端提供服务的攻击。在Scott Crosby演示的这个攻击的具体案例中,攻击可能会使目标系统充斥大量的数据,这些数据的哈希冲突导致目标系统使用更多的计算能力来解决冲突。

Python中可以hash的数据类型


在Python中,仅仅只有不可变数据类型可以被hash,然而每个自定义的对象在Python中都可以被hash,默认的他们的hash值是由他们的id派生的。也就意味着,同一个类的两个不同实例,默认的是得到不同的hash值

>>> class Car():
... velocity = 0
... direction = 0
... damage = 0
...
>>> first_car = Car()
>>> second_car = Car()
>>> hash(first_car)
274643597
>>> hash(second_car)
274643604

哈希表


现在你知道了什么是哈希函数,现在可以检测哈希表,哈希表是一个数据结构可以储存一堆键值对。

在哈希表中,键值对的所有建必须是可以哈希的,因为存储的对是通过使用其键的散列索引的。哈希表十分有用,Hash tables are very useful because the average number of instructions that are necessary to lookup an element of the table is independent of the number of elements stored in the table itself.哈希表非常有用,因为查找表中某个元素所需的平均指令数量与表中存储的元素数量无关,这就表明了不管你的表增长到成百上千次,查找特定元素的速度不会受到影响。

哈希表通常是通过创建可变数量的存储桶来实现的,这些存储桶将包含您的数据,并通过哈希它们的键对这些数据进行索引。键的散列值将确定用于特定数据段的正确存储桶。

import pprint

class Hashtable:
def __init__(self, elements):
self.bucket_size = len(elements)
self.buckets = [[] for i in range(self.bucket_size)]
self._assign_buckets(elements) def _assign_buckets(self, elements):
for key, value in elements:
hashed_value = hash(key)
index = hashed_value % self.bucket_size
self.buckets[index].append((key, value)) def get_value(self, input_key):
hashed_value = hash(input_key)
index = hashed_value % self.bucket_size
bucket = self.buckets[index]
for key, value in bucket:
if key == input_key:
return(value)
return None def __str__(self):
return pprint.pformat(self.buckets) # here pformat is used to return a printable representation of the object if __name__ == "__main__":
capitals = [
('France', 'Paris'),
('United States', 'Washington D.C.'),
('Italy', 'Rome'),
('Canada', 'Ottawa')
]
hashtable = Hashtable(capitals)
print(hashtable)
print(f"The capital of Italy is {hashtable.get_value('Italy')}")

Moreover, the more you increase the number of buckets you will handle, the more space you will waste. To test this you can simply change the bucket size of your previous example using a number of buckets that is two times the length of the input list:

此外,处理的桶数增加越多,浪费的空间就越多。要测试这一点,只需使用输入列表长度的两倍的桶数来更改上一个示例的桶大小

两个散列值发生碰撞,将会存储到同一个桶中,因为冲突不可避免,实现一个哈希表就得有一个解决冲突的方法。

通常在哈希表解决冲突的常用策略是:

  • open addressing 开放寻址法
  • separate chaining 链地址法

连地址法是您在上面的示例中已经实现的,它由使用另一个数据结构在同一个bucket中创建一个值链组成。在那个示例中,您使用了一个嵌套列表,当在超额占用的bucket中查找特定值时,必须对该列表进行完全扫描。

在开放寻址策略中,如果您应该使用的bucket是忙碌的,那么您只需继续搜索要使用的新bucket。要实现这个解决方案,您需要对为新元素分配bucket的方式和检索键值的方式进行一些更改。从assign buckets()函数开始,您必须使用默认值初始化您的bucket,并且如果您应该使用的bucket已经被占用,则继续寻找空的bucket

Dictionaries in Python are built using hash tables and the **open addressing** collision resolution method.

Python 中的哈希表的更多相关文章

  1. 【转】实习小记-python中可哈希对象是个啥?what is hashable object in python?

    [转]实习小记-python中可哈希对象是个啥?what is hashable object in python? 废话不多说直接祭上python3.3x的文档:(原文链接) object.__ha ...

  2. [PHP内核探索]PHP中的哈希表

    在PHP内核中,其中一个很重要的数据结构就是HashTable.我们常用的数组,在内核中就是用HashTable来实现.那么,PHP的HashTable是怎么实现的呢?最近在看HashTable的数据 ...

  3. 实习小记-python中不可哈希对象设置为可哈希对象

    在这篇之前,我又专门翻译过官方python3.3的可哈希对象文档,大家可以先参考一下: 实习小记-python中可哈希对象是个啥?what is hashable object in python? ...

  4. Delphi 中的哈希表: THashedStringList

    转自万一博客 Delphi 中的哈希表: THashedStringList unit Unit1; interface uses   Windows, Messages, SysUtils, Var ...

  5. 实习小记-python中可哈希对象是个啥?what is hashable object in python?

    废话不多说直接祭上python3.3x的文档:(原文链接) object.__hash__(self) Called by built-in function hash() and for opera ...

  6. python数据结构之哈希表

    哈希表(Hash table) 众所周知,HashMap是一个用于存储Key-Value键值对的集合,每一个键值对也叫做Entry.这些个键值对(Entry)分散存储在一个数组当中,这个数组就是Has ...

  7. 数据结构与算法Python版 熟悉哈希表,了解Python字典底层实现

    Hash Table 散列表(hash table)也被称为哈希表,它是一种根据键(key)来存储值(value)的特殊线性结构. 常用于迅速的无序单点查找,其查找速度可达到常数级别的O(1). 散列 ...

  8. OVS 中的哈希表: shash

    shash出现在OVS的代码中,定义如下:   struct hmap_node {     size_t hash;     struct hmap_node * next; };   struct ...

  9. Freemarker 中的哈希表(Map)和序列(List)

    freemarlker中的容器类型有: 哈希表:是实现了TemplateHashModel或者TemplateHashModelEx接口的java对象,经常使用的实现类是SimpleHash,该类实现 ...

随机推荐

  1. close与shutdown

    首先看一个例子,如下图所示: 当我们客户端发送ABCD再close套接字的时候,服务器端的接收通道也被关闭了,将无法接收ABCD的数据.如果想要仅仅关闭发送通道,保留接收通道,可以使用shutdown ...

  2. Java入门基础知识点总结(详细篇)

    Java入门基础知识点总结(详细篇)~~~~~目录 1.1 图解 1.1.1 Java基础知识点 1.1.2 Java基础语法的相关内容 1.2 关键字 1.3 标识符 1.3.1 标识符概念 1.3 ...

  3. SQL SERVER数据库Left Join用法

    Left Join基本语法: SQL LEFT JOIN 关键字 LEFT JOIN 关键字会从左表 (table_name1) 那里返回所有的行,即使在右表 (table_name2) 中没有匹配的 ...

  4. 应届生应聘阿里,腾讯,美团90%会被问到的Netty面试题!史上最全系列!

    1.BIO.NIO 和 AIO 的区别? BIO:一个连接一个线程,客户端有连接请求时服务器端就需要启动一个线程进行处理.线程开销大.伪异步 IO:将请求连接放入线程池,一对多,但线程还是很宝贵的资源 ...

  5. 怎么用Folx自动标签功能自动分类文件

    Folx标签功能可以帮助职场人士提高文件分类的效率.通过使用自动标签功能,用户可以在文件下载时,自动为相关的文件进行标签分类.接下来,小编会以创建"软件"自动标签为例,为大家演示相 ...

  6. 【VUE】7.Vuex基本使用

    1. 安装Vuex npm install vuex --save 2. 导入Vuex包 import Vuex from 'vuex' Vue.use(Vuex) 3. 创建store对象 cons ...

  7. 简化的鸿蒙WiFi接口,仅需几行代码,简单易用!

    使用鸿蒙原始WiFI API接口进行编程,整个过程稍显繁琐,为此我们对鸿蒙原始WiFi API接口做了一层封装,形成了一套更简单易用的接口. 简化后的API接口 STA模式 // 连接WiFi热点,并 ...

  8. Java中的单例模式最全解析

    单例模式是 Java 中最简单的设计模式之一,它是指一个类在运行期间始终只有一个实例,我们就把它称之为单例模式.它不但被应用在实际的工作中,而且还是面试中最常考的题目之一.通过单例模式我们可以知道此人 ...

  9. SQL注入步骤

    1.判断是否存在注入,注入是字符型还是数字型2.猜解SQL查询语句中的字段数3.确定回显的字段数4.获取当前数据库5.获取表中字段名6.下载数据

  10. python应用(6):函数

    在流程很简单的时候,或者流程不简单但我们不需要考虑开发维护成本的时候,平面地组织你的代码就够了,不用费脑子,不需要考虑层次或重用的东西.当事情变得越来越复杂时,当同行对代码质量要求越来越高时,有一些重 ...