V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
bccber
V2EX  ›  Python

用类作字典的 Key 能搞不?

  •  
  •   bccber · 2017-07-26 11:38:01 +08:00 · 4509 次点击
    这是一个创建于 2465 天前的主题,其中的信息可能已经有所发展或是发生改变。
    class Key:
    def __init__(self, i, j):
    self.i = i
    self.j = j
    pass

    dic={};

    key1 = Key(1, 1)
    key2 = Key(1, 1)

    dic.append(key1, 1)
    dic.append(key2, 1)
    22 条回复    2017-07-26 22:04:29 +08:00
    fox0001
        1
    fox0001  
       2017-07-26 12:01:33 +08:00 via Android
    那要 key 来干嘛?
    wwqgtxx
        2
    wwqgtxx  
       2017-07-26 12:10:34 +08:00
    当然可以了
    Python 3.6.1 (v3.6.1:69c0db5, Mar 21 2017, 18:41:36) [MSC v.1900 64 bit (AMD64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> class A:
    ... pass
    ...
    >>> {A():1,A():2}
    {<__main__.A object at 0x0000026C84DA32E8>: 1, <__main__.A object at 0x0000026C84DA3320>: 2}
    >>>
    pythongo
        3
    pythongo  
       2017-07-26 12:17:45 +08:00
    字典的 key 是不可变,能 hash 的, 你去看字典 key 的限制,只要符合规范当然可以
    siteshen
        4
    siteshen  
       2017-07-26 12:20:55 +08:00
    1. 代码能不能跑?跑一下就知道了。
    2. 为什么能(不能)跑?参考 3 楼的答案。
    ivechan
        5
    ivechan  
       2017-07-26 12:42:12 +08:00
    能 hash 的都能.
    bccber
        6
    bccber  
    OP
       2017-07-26 13:42:59 +08:00
    @ivechan @pythongo @wwqgtxx
    怎么处理碰撞?
    当类的 i,j 字段不一样 但 hash 又一样时,怎么处理?
    需要重写 HashCode?或者 Equals 方法吗?
    HypoChen
        7
    HypoChen  
       2017-07-26 13:52:24 +08:00
    bccber
        8
    bccber  
    OP
       2017-07-26 13:54:00 +08:00
    def __hash__(self): 和 __eq__ ??
    bccber
        9
    bccber  
    OP
       2017-07-26 13:54:18 +08:00
    @HypoChen 你输了五毛
    PythonAnswer
        10
    PythonAnswer  
       2017-07-26 14:00:24 +08:00 via Android
    汽车能当钥匙使吗?能啊。但是有些浪费。
    cy18
        11
    cy18  
       2017-07-26 14:03:35 +08:00 via Android
    hash 是辅助判断的,主要是 eq。要确保 eq 的两个对象 hash 必须相等,反之不一定。
    最坏情况,hash 全部返回 0 都没关系,只不过会导致速度很慢。
    反之如果 hash 不等但是 eq 的话就会出问题。比如用 id 做 hash 然后用成员变量值判断 eq。
    bccber
        12
    bccber  
    OP
       2017-07-26 14:04:54 +08:00
    hash 只是为了快速判断
    当 hash 一样 会使用 eq 再判断
    是吧
    bccber
        13
    bccber  
    OP
       2017-07-26 14:06:53 +08:00
    ok 了
    bccber
        14
    bccber  
    OP
       2017-07-26 14:07:48 +08:00
    @PythonAnswer 你竟然没碰到过必须要使用类或者结构体作 key 的场景?
    CryMeatel
        15
    CryMeatel  
       2017-07-26 14:15:17 +08:00
    @bccber 一般这样的需求,我都是直接把数据套入现成可 hash 化容器的,比如 tuple, namedtuple ;其实类作为 key 也是为了对其成员变量做个区分吧
    bccber
        16
    bccber  
    OP
       2017-07-26 14:48:50 +08:00
    @CryMeatel

    我猜测 tuple 并不会处理 def __hash__(self): 和 __eq__
    然后是使用 tuple 的地址作 key 这样有可能会出问题的
    wwqgtxx
        17
    wwqgtxx  
       2017-07-26 17:12:08 +08:00 via iPhone
    @bccber python 的 dict 并不考虑你的__hash__和__eq__,他是直接比较内存地址的
    wwqgtxx
        18
    wwqgtxx  
       2017-07-26 18:12:47 +08:00
    刚才没注意,python 的 dict 会考虑__eq__的,测试方法如下
    >>> class A:
    ... def __hash__():
    ... return 1
    ...
    >>>
    >>> class A:
    ... def __hash__(self):
    ... return 1
    ... def __eq__(self,a):
    ... return True
    ...
    >>> {A():1,A():2}
    {<__main__.A object at 0x0000026C84DA3D30>: 2}
    >>> class A:
    ... def __hash__(self):
    ... return 1
    ...
    >>> {A():1,A():2}
    {<__main__.A object at 0x0000026C84DA3320>: 1, <__main__.A object at 0x0000026C84DA3D68>: 2}
    >>>
    pythongo
        19
    pythongo  
       2017-07-26 19:32:02 +08:00
    A dictionary ’ s keys are almost arbitrary values. Values that are not hashable, that is, values containing lists, dictionaries or other mutable types (that are compared by value rather than by object identity) may not be used as keys.

    hashable
    An object is hashable if it has a hash value which never changes during its lifetime (it needs a __hash__() method), and can be compared to other objects (it needs an __eq__() or __cmp__() method). Hashable objects which compare equal must have the same hash value.

    Hashability makes an object usable as a dictionary key and a set member, because these data structures use the hash value internally.

    All of Python ’ s immutable built-in objects are hashable, while no mutable containers (such as lists or dictionaries) are. Objects which are instances of user-defined classes are hashable by default; they all compare unequal (except with themselves), and their hash value is derived from their id().
    PythonAnswer
        20
    PythonAnswer  
       2017-07-26 19:54:17 +08:00 via Android
    @bccber 没遇到过。并且你这个只是实例做 key。

    key 应该尽量简化,value 则可以复杂些。个人愚见。
    CryMeatel
        21
    CryMeatel  
       2017-07-26 19:58:31 +08:00
    @bccber 哈哈, 但实际上 tuple 是 python 文档里面推荐的可 hash 可对比的不变容器,用来做 Key 合适不过了。

    不过你一定要确认的话,tuple 实现都是 C 代码,要看去看解释器源码了。
    linw1995
        22
    linw1995  
       2017-07-26 22:04:29 +08:00
    >>> from collections.abc import Hashable
    >>> mutable = [list, bytearray, set, dict]
    >>> immutable = [int, float, complex, str, tuple, frozenset, bytes]
    >>> all(isinstance(x(), Hashable) for x in immutable)
    True
    >>> all(issubclass(x, Hashable) for x in immutable)
    True
    >>> any(isinstance(x(), Hashable) for x in mutable)
    False
    >>> any(issubclass(x, Hashable) for x in mutable)

    基本类型里,不可变的都是可 hash 的。无论是有序的 Tuple 还是无序的 frozenset,因为其是不可变的,都是可 hash 的。所以都可以当 mapping 的 key。
    若是要自定义可 hash 的对象,就一定要实现__hash__、__eq__两个方法。
    若是不实现,也可以 hash。这是因为类缺省__hash__、__eq__两个方法,但是其依据是 id 值,那么结果就不是我们想要的。

    参考:
    https://wiki.python.org/moin/DictionaryKeys#User_Defined_Types_as_Dictionary_Keys
    http://www.laurentluce.com/posts/python-dictionary-implementation/
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5392 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 07:24 · PVG 15:24 · LAX 00:24 · JFK 03:24
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.