Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/365.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 设置基于_散列的隐式顺序的更改?_Python_Python 3.x_Hash_Set_Python Internals - Fatal编程技术网

Python 设置基于_散列的隐式顺序的更改?

Python 设置基于_散列的隐式顺序的更改?,python,python-3.x,hash,set,python-internals,Python,Python 3.x,Hash,Set,Python Internals,我刚刚在Python3中观察到一组有趣的行为,我想知道为什么 鉴于班级: class Tab: @staticmethod def set(size): return set(map(lambda label: Tab(label), range(1, size + 1))); def __init__(self, label): self.label = label self.up = True def __

我刚刚在Python3中观察到一组有趣的行为,我想知道为什么

鉴于班级:

class Tab:

    @staticmethod
    def set(size):
        return set(map(lambda label: Tab(label), range(1, size + 1)));

    def __init__(self, label):
        self.label = label
        self.up = True

    def __eq__(self, other):
        if not isinstance(other, Tab):
            return NotImplemented
        return self.label == other.label

    def __hash__(self):
        return hash(self.label)

    def __str__(self):
        return str(self.label)
当我调用
Tab.set(9)
时,我会得到一组选项卡,当通过以下方式表示为字符串时:

"|%s|" % "|".join([str(tab) for tab in self.tabs])
生成:

|1|2|3|4|5|6|7|8|9|
但是,如果我只修改
\uuuuu eq\uuuu
\uuuu hash\uuuu
以合并
up
属性:

def __eq__(self, other):
    if not isinstance(other, Tab):
        return NotImplemented
    return self.label == other.label and self.up == other.up

def __hash__(self):
    return hash((self.label, self.up))
集合的隐式顺序更改,字符串表示形式变为:

|9|8|7|6|5|4|3|2|1|
我知道这几套是没有订的。但是,当静态
set
方法不变时,为什么隐式排序会发生变化,从1到9创建集合中的每个元素,就像以前一样

还有,我可以做些什么来保持隐式排序,使我的集合看起来像以前一样有序?(请注意,此更改是由
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

当静态
set
方法不变时,为什么隐式排序会发生变化,从1到9创建集合中的每个元素,就像以前一样

此方法不变,但使用
Tab
对象调用内置的
set

而且由于
\uuuuuuuuuuuuuuuuuuuuuuuuuuuu
方法已更改,
set
可能会更改您无论如何都不应该依赖的内部顺序

在您的情况下,打印时进行排序是可行的:

"|%s|" % "|".join([str(tab) for tab in sorted(self.tabs,lambda t:(t.label, t.up)])
或者在没有lambda的情况下,定义
\uu lt\uu
方法,以便
排序
可以比较对象

当静态
set
方法不变时,为什么隐式排序会发生变化,从1到9创建集合中的每个元素,就像以前一样

此方法不变,但使用
Tab
对象调用内置的
set

而且由于
\uuuuuuuuuuuuuuuuuuuuuuuuuuuu
方法已更改,
set
可能会更改您无论如何都不应该依赖的内部顺序

在您的情况下,打印时进行排序是可行的:

"|%s|" % "|".join([str(tab) for tab in sorted(self.tabs,lambda t:(t.label, t.up)])
或者在没有lambda的情况下,定义
\uu lt\uu
方法,以便
排序
可以比较对象

为什么隐式排序发生了变化

因为
set
是在CPython中实现的。因此:

  • 项目存储在
    集中的插槽取决于项目的(截断的)散列(在某种程度上还取决于插入顺序,因为插槽可能已经被占用)
  • 项的哈希值取决于该项的方法
如果迭代
集合
,则逐槽迭代哈希表(不包括缺少的条目)。因此,通过更改哈希,您可以更改顺序

在您的情况下,散列是不同的,因为您更改了
\uuuuuuuuuuuuuuuuu散列
方法实现,因此预期顺序不同:

>>[tab.set(9)中tab的哈希(tab)]#第一个代码
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>[tab.set(9)中tab的哈希(tab)]#第二个代码
[3713072971709512581, 3713088127104978631, 3713071889183430056, 3713087044578896106, 3713083796991988331, 3713082714465905806, 3713085962048483481, 3713084879522400956, 3713081631935493181]
我该怎么做才能保持隐式排序,使我的集合看起来像以前一样有序

如果您希望对它们进行排序,我的建议是不要使用
集合
-使用已排序的集合,举个例子:
列表
。还有一些方法可以提高效率

但是,如果要保留
集合
,并希望它们按
标签
属性排序,也可以使用
排序

sorted(tab.set(9), key=lambda t: t.label)

>>> [str(t) for t in sorted(Tab.set(9), key=lambda t: t.label)]
['1', '2', '3', '4', '5', '6', '7', '8', '9']

使用Cython检查CPython 3.7.4哈希表 注意:这是检查可能在不同版本之间更改的实施细节。Cython代码甚至可能不适用于不同的CPython版本不要从字面上理解它们,永远不要依赖它们。

如果您对CPython的实际实现细节感兴趣,您可以使用我在中编写的Jupyter Cython助手:

%load\u ext Cython
%%赛昂
从cpython cimport PyObject,PyTypeObject
西姆波特赛顿酒店
“Python.h”中的cdef外部:
ctypedef Py_ssize_t Py_hash_t
结构设置项:
PyObject*键
Py_hash_t hash
ctypedef结构PySetObject:
Py_-ssize_-t ob_-refcnt
PyTypeObject*ob_类型
填充
未使用的Py_-ssize
Py_-ssize_-t遮罩
setentry*表
Py_hash_t hash
粗手指
setentry小型表[8]
PyObject*weakreflist
cpdef打印设置表(设置输入):
cdef PySetObject*innerset=inp
对于范围内的idx(innerset.mask+1):
if(innerset.table[idx].key==NULL):
打印(idx,“”)
其他:
打印(idx,innerset.table[idx].hash,innerset.table[idx].key)
这将打印内部哈希表,每行包含插槽号、哈希和存储项

在第一种情况下:

>>> print_set_table(Tab.set(9))
0 <EMPTY>
1 1 1
2 2 2
3 3 3
4 4 4
5 5 5
6 6 6
7 7 7
8 8 8
9 9 9
10 <EMPTY>
11 <EMPTY>
[...]
30 <EMPTY>
31 <EMPTY>
>>打印集合表格(集合选项卡(9))
0
1 1 1
2 2 2
3 3 3
4 4 4
5 5 5
6 6 6
7 7 7
8 8 8
9 9 9
10
11
[...]
30
31
第二种情况:

>>> print_set_table(Tab.set(9))
0 <EMPTY>
[...]
4 <EMPTY>
5 3713072971709512581 9
6 <EMPTY>
7 3713088127104978631 7
8 3713071889183430056 8
9 <EMPTY>
10 3713087044578896106 6
11 3713083796991988331 3
12 <EMPTY>
13 <EMPTY>
14 3713082714465905806 2
15 <EMPTY>
[...]
24 <EMPTY>
25 3713085962048483481 5
26 <EMPTY>
27 <EMPTY>
28 3713084879522400956 4
29 3713081631935493181 1
30 <EMPTY>
31 <EMPTY>
>>打印集合表格(集合选项卡(9))
0
[...]
4.
5 3713072971709512581 9
6.
7 3713088127104978631 7
8 3713071889183430056 8
9
10 3713087044578896106 6
11 3713083796991988331 3
12
13
14 3713082714465905806 2
15
[...]
24
25 3713085962048483481 5
26
27
28 3713084879522400956 4
29 3713081631935493181 1
30
31
为什么隐式排序发生了变化

因为
set
是在CPython中实现的。因此:

  • 项目存储在
    集中的插槽取决于项目的(截断的)散列(在某种程度上还取决于插入顺序,因为插槽可能已经被占用)
  • 项的哈希值取决于该项的方法
如果迭代
集合
,则逐槽迭代哈希表(不包括缺少的条目)。所以由查