精华内容
下载资源
问答
  • list为什么不能作为字典的key

    千次阅读 2018-07-26 16:24:02
    List为什么不能作为key 至于这个问题,最直接的答案就是:list没有支持__hash__方法,那么为什么呢? 对于list的hash函数,我们可能有下面两种实现的方式: 第一种,基于id。这满足条件,“如果hash值不同,那么...

    List为什么不能作为key

    至于这个问题,最直接的答案就是:list没有支持__hash__方法,那么为什么呢?

    对于list的hash函数,我们可能有下面两种实现的方式:

    第一种,基于id。这满足条件,“如果hash值不同,那么他们的id当然不同”。但考虑到list一般是作为容器,基于id来hash可能会导致下面两种情况:

    用相同的list作为key去字典中找某个元素可能会得到不同的结果,因为是基于id hash的,所以即使他们的内容相同,字典依然将他们作为不同的元素对待。 创建一个一模一样的list用字典查找永远会得到一个KeyError。

    第二种,基于内容。tuple就是这样做的,但是要注意一点,list是可以修改的。当list修改之后,你就永远别想再从字典中拿回来了。见下面的代码。

    >>> l = [1, 2] >>> d = {} >>> d[l] = 42 >>> l.append(3) >>> d[l] # 原来的hash值是基于[1, 2]hash的, # 现在是基于[1, 2, 3],所以找不到 Traceback (mostrecentcalllast): File "", line 1, in ? KeyError: [1, 2, 3] >>> d[[1, 2]] # 基于hash [1, 2] # 但是遍历的时候找不到key相等的键值对 #(因为字典里的key变成了[1, 2, 3] Traceback (mostrecentcalllast): File "", line 1, in ? KeyError: [1, 2]

    鉴于两种实现的方式都存在一定的副作用,所以Python规定:

    内置的list不能作为字典的key.

    但tuple是不可变,所以tuple可以作为字典的key。



    作者:Darker_坤
    链接:https://www.jianshu.com/p/56691574ef9e
    來源:简书
    简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

    展开全文
  • 有时有必要将集合用作字典键,但是这可能会导致...本文旨在您提供一种将列表用作字典键的有效且相对安全的方法。 概念化这个混乱 字典键必须具有可比性,以确保相等性,必须提供适当的哈希码,并且必须可变。...

    目录

    介绍

    概念化这个混乱

    编码此混乱

    警告

    使用其他容器


    有时有必要将集合用作字典键,但是这可能会导致性能下降并且不安全。以下是使它更快更安全的方法。

    介绍

    使用C#进行功能样式编程时,您最终可能会遇到需要按项目的有序列表来键入字典的情况。有好的方法,但不是非常好的方法。本文旨在为您提供一种将列表用作字典键的有效且相对安全的方法。

    概念化这个混乱

    字典键必须具有可比性,以确保相等性,必须提供适当的哈希码,并且必须不可变。这些要求使使用列表作为字典键有点麻烦,因为列表通常不提供值语义——在这种情况下,是逐项比较和适当的哈希码。也许更糟的是,列表允许您随时修改它们。

    总之,这里有两个主要问题,一个是逐项比较列表不是很有效,并且使用标准IList<T>实现作为字典键从根本上是不安全的,因为可以随时对其进行修改。

    我们可以通过要求使用IReadOnlyList<T>来或多或少地解决后者,后者提供类似列表的访问,而不需要修改列表的方法。不过,当我们声明KeyList<T>类时,我们将在其中添加一个Add()方法。这样做的原因是允许您首先填写列表。如果我们想更加安全,则可以取消该Add()方法,并强制您将这些项作为数组传递给KeyList<T>的构造函数。未执行此操作的原因是为了提高性能。KeyList<T>的主要目的是性能,将数据传递给构造函数将需要一个副本。副本不一定很慢,但我们不需要它——我们在这里没事,因为任何采用IReadOnlyList<T>的操作都不会看到Add() 方法,这样,我们就避免了虚假的副本。

    前者需要做更多的工作。我们必须在KeyList<T>上实现值相等语义。但是,在这里,我们提出了一个重要的技巧:在将每个项目添加到列表时,我们都会重新计算哈希码,这样我们就不必在以后进行计算。然后,我们将该哈希码用作相等性检查的一部分,以在它们不相等时短路。接下来,让我们看一下该KeyList<T>类的代码。

    编码此混乱

    sealed class KeyList<T> : IReadOnlyList<T>, IEquatable<KeyList<T>>
    {
        int _hashCode;
        List<T> _items;
        public KeyList()
        {
            _items = new List<T>();
            _hashCode = 0;
        }
        public KeyList(int capacity)
        {
            _items = new List<T>(capacity);
            _hashCode = 0;
        }
        public T this[int index] => _items[index];
    
        public int Count => _items.Count;
    
        public IEnumerator<T> GetEnumerator()
        {
            return _items.GetEnumerator();
        }
    
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
        public void Add(T item)
        {
            _items.Add(item);
            if (null != item)
                _hashCode ^= item.GetHashCode();
        }
        public override int GetHashCode()
        {
            return _hashCode;
        }
        public bool Equals(KeyList<T> rhs)
        {
            if (ReferenceEquals(this, rhs))
                return true;
            if (ReferenceEquals(rhs, null))
                return false;
            if (rhs._hashCode != _hashCode)
                return false;
            var ic = _items.Count;
            if (ic != rhs._items.Count)
                return false;
            for(var i = 0;i<ic;++i)
                if (!Equals(_items[i], rhs._items[i]))
                    return false;
            return true;
        }
        public override bool Equals(object obj)
        {
            return Equals(obj as KeyList<T>);
        }
    }

    这里的重要方法Add()和主要Equals()方法。您可以在Add()中看到我们正在为添加的每个项目修改_hashCode字段。您可以在Equals()方法中看到,如果哈希代码不相等,我们会在最后进行逐项比较之前短路。这样可以大大提高性能。另一个性能提升在于无需计算任何内容的GetHashCode()方法。由于这些方法通常是由IDictionary<TKey,TValue>实现调用的,因此我们希望它们尽可能快,尤其是GetHashCode()方法,Dictionary<TKey, TValue>经常调用它

    如果在发布模式下运行代码,则将获得准确的性能指标。在Debug中没有那么多,因此如果您在Debug中运行,该程序将向您发出警告。您可以看到我们使用的字典,一个是标准列表,另一个是特殊KeyList<T>实例。与使用List<T>相比,使用KeyList<T>应该可以提高5x+的性能, 而且由于它只实现了列表访问的IReadOnlyList<T>,所以也更安全。现在我们两个最初的问题都解决了。

    警告

    已经指出,如果T类型是可变的,则这是不正确的。这是真的。然而,Dictionary<TKey, TValue>HashSet<T>也是如此,所以这里也适用同样的警告-不要更改添加到这些列表的项的值,就像不应该更改添加到DictionaryHashSet的键值一样。

    使用其他容器

    如果需要,可以将该技术应用于其他集合。如果需要(相对地)高效的无序比较,可以将其调整为HashSet<T> 。我已经在一些项目中做到了这一点。只需确保在Equals()方法中使用SetEquals()进行逐项比较,因为它更快。

    展开全文
  • Python 为什么list不能作为字典的key?

    千次阅读 2019-04-11 23:50:38
    很多Python初学者经常会有这样的...为什么tuple可以作为字典的key,list可以?要理解这个问题,首先要明白python的字典工作原理。 1.Python的字典是如何工作的 在Python中,字典也就是一个个的“映射”,将k...

    原文地址https://www.kawabangga.com/posts/1821

     

    很多Python初学者经常会有这样的疑问,为什么Python有tuple(元组)和list(列表)两种类型?为什么tuple可以作为字典的key,list不可以?要理解这个问题,首先要明白python的字典工作原理。

    1.Python的字典是如何工作的

    在Python中,字典也就是一个个的“映射”,将key映射到value:

    1

    2

    # 对一个特定的key可以得到一个value

    value = d[key]

    为了实现这个功能,Python必须能够做到,给出一个key,找到哪一个value与这个key对应。先来考虑一种比较简单的实现,将所有的key-value键值对存放到一个list中,每当需要的时候,就去遍历这个list,用key去和键值对的key匹配,如果相等,就拿到value。但是这种实现在数据量很大的时候就变得很低效。它的算法复杂度是O(n),n是存放键值对的数量。(关于Hash表具体的工作原理,可以参考我的这篇文章

    为此,Python使用了hash(哈希)的方法来实现,要求每一个存放到字典中的对象都要实现hash函数,这个函数可以产生一个int值,叫做hash value(哈希值),通过这个int值,就可以快速确定对象在字典中的位置。然而,由于Hash碰撞的存在,可能存在两个对象的Hash值是相同的,所以查找字典的过程中,要比较hash值,还要比较value的值。

    这个查询的大致过程如下:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    def lookup(d, key):

        '''字典的查询过程概括为下面3步:

           1. 通过hash函数将key计算为哈希值.

     

           2. 通过hash值确定一个位置,这个位置是一个存放着

              可能存在冲突的元素的数组(很多地方叫做“桶”,bucket),

              每一个元素都是一个键值对,理想情况下,这个数组里只有1个元素.

     

           3. 遍历这个数组,找到目标key,返回对应的value.

        '''

        h = hash(key)                  # step 1

        cl = d.data[h]                 # step 2

        for pair in cl:                # step 3

            if key == pair[0]:

                return pair[1]

        else:

            raise KeyError, "Key %s not found." % key

    要使这个查找过程正常工作,hash函数必须满足条件:如果两个key产生了不同的hash value,那么这两个key对象是不相等的。

    1

    for all i1, i2, if hash(i1) != hash(i2), then i1 != i2

    否则的话,hash value不同,对象却相同,那么相同的对象产生不同的hash value,查找的时候就会进错桶(step 2),在错误的桶里永远也找不到你要找的value。

    另外,要让字典保持高查找效率,还要保证:当两个key产生相同的hash value,那么他们是相等的。

    1

    for all i1, i2, if hash(i1) == hash(i2), then i1 == i2

    这样做的目的是,尽量满足每个hash桶只有一个元素。为什么要这样呢? 考虑下面这个hash函数。

    1

    2

    def hash(obj):

        return 1

    这个hash函数是满足上面我们谈的第一个条件的:如果两个key的hash value不同,那么两个key对象不相同。因为所有的对象产生的hash value都是1,所以不存在能产生不同hash value的key,也就不存在不满足的情况。但是这样做的坏处是,因为所有的hash value都相同,所以就把所有的对象分到了同一个地方。查找的时候,进行到第三步,遍历的效率就变成了O(n).

    Hash函数应该保证所有的元素平均的分配到每一个桶中,理想的情况是,每一个位置只有一个元素。

    以上两个原则,第一个保证了你能从字典中拿到要找的元素,第二个保证了查询效率。

    2.字典Key要满足的要求

    经过上面的讨论,我们应该明白Python为什么对字典的key有这样的要求了:

    要作为字典的key,对象必须要支持hash函数(即__hash__),相等比较(__eq__或__cmp__),并且满足上面我们讨论过的条件。

    3.List为什么不能作为key

    至于这个问题,最直接的答案就是:list没有支持__hash__方法,那么为什么呢?

    对于list的hash函数,我们可能有下面两种实现的方式:

    第一种,基于id。这满足条件——“如果hash值不同,那么他们的id当然不同”。但考虑到list一般是作为容器,基于id来hash可能会导致下面两种情况:

    • 用相同的list作为key去字典中找某个元素可能会得到不同的结果,因为是基于id hash的,所以即使他们的内容相同,字典依然将他们作为不同的元素对待。
    • 创建一个一模一样的list用字典查找永远会得到一个KeyError。

    第二种,基于内容。tuple就是这样做的,但是要注意一点,tuple是不可以修改的,但list是可以修改的。当list修改之后,你就永远别想再从字典中拿回来了。见下面的代码。

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    >>> l = [1, 2]

    >>> d = {}

    >>> d[l] = 42

    >>> l.append(3)

    >>> d[l] # 原来的hash值是基于[1, 2]hash的,

             # 现在是基于[1, 2, 3],所以找不到

    Traceback (most recent call last):

      File "<interactive input>", line 1, in ?

    KeyError: [1, 2, 3]

    >>> d[[1, 2]] # 基于hash [1, 2]

                  # 但是遍历的时候找不到key相等的键值对

                  #(因为字典里的key变成了[1, 2, 3]

    Traceback (most recent call last):

      File "<interactive input>", line 1, in ?

    KeyError: [1, 2]

    鉴于两种实现的方式都存在一定的副作用,所以Python规定:

    内置的list不能作为字典的key.

    但tuple是不可变,所以tuple可以作为字典的key。

    (2018年1月2日更新,上面我说tuple不可变可以作为字典的key,这句话并不是完全正确的。tuple只是相对不可改变的,如果tuple中有元素是可变对象,那么虽然tuple不可改变,那么其中元素所指向的对象是可变的,所以同样会出现上面“list不能作为字典的key”这个问题,即含有可变对象的tuple也不能作为字典的key,举个例子就很好懂了。)

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    In [11]: li = [1,2,]

     

    In [12]: d = dict()

     

    In [13]: t2 = (1,2,)

     

    In [14]: t3 = (1,2,li,)

     

    In [15]: d[li] = 1

    ---------------------------------------------------------------------------

    TypeError                                 Traceback (most recent call last)

    <ipython-input-15-cc334e53316a> in <module>()

    ----> 1 d[li] = 1

     

    TypeError: unhashable type: 'list'

     

    In [16]: d[t2] = 2

     

    In [17]: d[t3] = 3

    ---------------------------------------------------------------------------

    TypeError                                 Traceback (most recent call last)

    <ipython-input-17-c9021fe91ba8> in <module>()

    ----> 1 d[t3] = 3

     

    TypeError: unhashable type: 'list'

     

    4.自定义的类型作为字典的Key

    用户自定义的类型就可以作为key了,默认的hash(object)是 id(object), 默认的cmp(object1, object2)是cmp(id(object1), id(object2)),同样是可以修改的对象,为什么这里就没有上面说的问题呢?

    1. 一般来说,在映射中比较常见的需求是用一个object替换掉原来的,所以id比内容更重要,就可以基于id来hash
    2. 如果内容重要的话,自定义的类型可以通过覆盖__hash__函数和__cmp__函数或__eq__函数来实现

    值得注意的是:将对象和一个value关联起来,更好的做法是将value设置为对象的一个属性。

    展开全文
  • 字典中的可以是元组,但不能为列表,因为元组是可变的,而列表是可变的。 python中要求字典中的可变的,如字符串、数字或元组,而值则可以取任何数据类型。 当字典键为列表时,系统会报错: ...

    字典中的键可以是元组,但不能为列表,因为元组是不可变的,而列表是可变的。
    python中要求字典中的键是不可变的,如字符串、数字或元组,而值则可以取任何数据类型。

    当字典的键为列表时,系统会报错:

    在这里插入图片描述

    展开全文
  • 字典中的可以是元组,但不能为列表,因为元组是可变的,而列表是可变的。 python中要求字典中的可变的,如字符串、数字或元组,而值则可以取任何数据类型。 当字典键为列表时,系统会报错: ...
  • Python中字典的排序分为按“”排序和按“值”排序。下面话多说了,来一起看看详细的介绍吧 问题描述 我们有一个字典列表,想根据一个或多个字典中的值来对列表排序。 例如,有如下字典列表,根据字典中的x,由...
  • 参考文献 ...这里是最权威的官方解释。 大概就是:设计就是这样的,...所以list是没有__hash__方法的(None),故而不能字典的key。 但是tuple有__hash__方法,所以可以做字典的key。 感觉这个网站非常好:https:/...
  • In Python 2.7 , I could get dictionary keys , values , or items as a list: 在Python 2.7中 ,我可以将字典键
  • python3 通过列表作为键 产生字典

    千次阅读 2019-08-16 14:05:26
    print(dict.fromkeys(['a','b','c'])) >>>{'a': None, 'b': None, 'c': None}
  • fromkeys() 函数用于创建一个新字典,以序列 seq 中元素做字典,value 为字典所有对应的初始值 基本语法: dict.fromkeys(seq[, value]) seq -- 字典键值列表。 value -- 可选参数, 设置序列(seq)...
  • 列表生成式 list = [i for i in range(10)]#for用来表示循环生成元素的个数。 list = [(i,j) for i in range (3) for j in range(2)]#后一个for嵌套于前一个for循环之中。 ...字典的类型...
  • Python 列表&字典作为函数的参数

    千次阅读 2018-09-06 14:38:13
    这篇笔记主要记录,列表作为函数参数传递时的深浅拷贝问题 #!/usr/bin/env python # -*- coding: utf-8 -*- ''' 这篇笔记主要记录,列表作为函数参数传递时的深浅拷贝问题 ''' import copy def list_append(l=...
  • 列表生成式 取下标i的和i+1 的 然后循环 起始值设0,终止值设为列表的长度 设置步长2后的值 02468而 i+1的值则13579 print({list[i]:list[i+1] for i in range(0,len(list),2)}) ...
  • 字典

    千次阅读 2019-08-15 21:56:37
    字典必须是一个可变类型,可以是一个数字,元组,字符串,但不能列表
  • Python列表元素为字典时,如何根据其中某个相同的键值进行元素合并
  • 致 Python 初学者

    万次阅读 多人点赞 2019-11-10 00:03:26
    欢迎来到“Python进阶”专栏!来到这里的每一位同学,应该大致上学习了很多 Python 的基础知识,正在努力成长的过程中。在此期间,一定遇到了很多的困惑,对未来的学习方向感到迷茫。...希望我的经验真正帮到你
  • Python 字典 fromkeys() 函数用于创建一个新字典,以序列 seq 中元素做字典,value 为字典所有对应的初始值。 fromkeys()方法语法               dict.fromkeys(seq, ...
  • 在Python中将列表转换为字典的10种方法

    万次阅读 多人点赞 2020-09-08 06:27:09
    我们可以使用切片将替代键列表,值作为项转换为字典。 Slicing returns a new list containing a sequence of items from the list. We can specify a range of indexes. 切片将返回一个新列表,其中包含列表中的一...
  • 先说函数:dict(zip(列表1,列表2)) 列表1作为key,列表2作为value 下面看一下代码: ...#合并为字典,调用dict(zip()) dict_name = dict(zip(list1,list2)) print(dict_name) 运行结果:  {0: 10, 1...
  • 20行Python代码爬取王者荣耀全英雄皮肤

    万次阅读 多人点赞 2019-11-21 22:01:03
    王者荣耀大家都玩过吧,没玩过的也应该听说过,作为时下最火的手机MOBA游戏,咳咳,好像跑题了。我们今天的重点是爬取王者荣耀所有英雄的所有皮肤,而且仅仅使用20行Python代码即可完成。 准备工作 爬取皮肤本身并...
  • python中字典按键或键值排序

    千次阅读 2018-11-20 19:40:23
    Python中字典的排序分为按“”排序和按“值”排序。 按“值”排序 按“值”排序就是根据字典的值进行排序,可以使用内置的sorted()函数。 sorted(iterable[, cmp[, key[, reverse]]]) iterable:...
  • [python]使用对象作为字典的key

    千次阅读 2020-04-15 22:31:22
    在`python`的字典中,值可以取任何数据类型,但(key)必须是可变的,如字符串,数字或元组,这就造成了一些麻烦。要使用对象作为字典的key,需要借助一个内建函数。
  • 将list列表转化为字典

    万次阅读 2018-12-29 09:43:48
    1、list1 = ['key1','key2'] list2 = ['value1','value2']  ====>dict(zip(list1,list2) 2、list = [['key1','value1'], ['key1','value... list[i[0]]=i[1] #字典赋值,左边key,右边value    
  • python字典转换为列表

    千次阅读 2020-08-24 12:10:15
    直接放代码,看得比较清晰 dict={'name':'wsm', 'age':'22', 'sex':'female'} print(list(dict)) ...list(dict)和list(dict.keys())几乎作用是一样,都是把key值作为list的元素 list(dict.values())是value(值
  • 将某班学生的《Python程序设计》这门课程的成绩保存在字典中,学号为键(key),分数值(value)。实现如下功能: (1)向字典中添加学生成绩。 (2)修改字典中指定学生成绩。 (3)删除指定学生成绩。 (4)查询...
  • 可以从任何以键值对作为元素的可迭代对象中构建出字典。 一般可以将元组的列表利用推导变成字典的数据类型。coding如下 country_code = {country:code for code,country in dial_codes} 推导的时候利用{} 在for之前...
  • python字典的键 字典中的值没有任何限制, 可以是任意Python对象,即从标准对象到用户自定义对象皆可... 当有键发生冲突(即字典键重复赋值),取最后(最近)的赋值。Python并会因字典中的键存在冲突而产生一个错...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 101,394
精华内容 40,557
关键字:

列表为什么不能作为字典的键