我正在寻找一种将给定值映射到数据 struct 中的键的元组的方法,以便datastructure[key1, key2]返回与datastructure[key2, key1]相同的值,而不必为键元组的每个Mutations 保存两次值.理想情况下,我正在寻找一种带有集合或"不区分顺序的元组"(我知道,根据定义元组是有序的)作为键的字典.

我的用例是:我需要以一种有效的方式保存字符串对的相似性值.因为sim(word1, word2)等于sim(word2, word1)(所以排序并不重要),所以我不想保存每个元组的值,因为这是多余的.相反,我希望 for each 单词对(元组键)保存一个值,并通过组成该对的单词的任一顺序来访问它.

目前,我正在这样做,它运行得很好:

d = {tuple(sorted(['w1', 'w2'])): 4}

print(d[tuple(sorted(['w1', 'w2']))])
print(d[tuple(sorted(['w2', 'w1']))])

>> 4
>> 4

但现在,每次更新和访问字典都需要列表初始化、排序操作和元组转换.

有没有一种很好的方法来做我想做的事情,而不需要使用上面的变通方法或定义一个定制的类作为键?也许有一种数据 struct 完全允许这样做,甚至可能更快,因为它是在比我的解决方案更低的级别上实现的. 理想情况下,如下所示:

d = {('w1', 'w2'): 4}

print(d[('w1','w2')])
print(d[('w2','w1')])

>> 4
>> 4

注:上面的例子不起作用,因为('a2','b2') != ('b2','a2').

以下是我到目前为止所做的研究,并解释了为什么这些解决方案不完全是我想要的:

如果存在重复的Stackoverflow问题而我忽略了它,我很感谢任何提示并提前找出借口-很难搜索这个问题,因为我不知道所需的功能将如何调用.

推荐答案

您可以使用帮助器函数以一种更便宜的方式创建排序的元组:

def key(s, t):
    return (s, t) if s < t else (t, s)

d = {key('w1', 'w2'): 4}

print(d[key('w1','w2')])
print(d[key('w2','w1')])

函数调用开销使其比内联表达式慢,但仍快于到目前为止提到的替代方法.

下面是计算它们并使用它们来查找dict值的各种表达式及其时间:

 132.7 ± 1.8 ns  (s, t) if s < t else (t, s)
 171.7 ± 1.5 ns  key(s, t)
 236.5 ± 0.9 ns  frozenset({s, t})
 254.9 ± 2.5 ns  frozenset((s, t))
 273.3 ± 2.0 ns  frozenset([s, t])
 343.2 ± 3.6 ns  tuple(sorted([s, t]))
 767.0 ± 9.7 ns  T(s, t)

基准代码(Attempt This Online!):

from timeit import timeit
from statistics import mean, stdev

setup = '''
s, t = 'w1', 'w2'
sts = [(s, t), (t, s)] * 5000

def key(s, t):
    return (s, t) if s < t else (t, s)

# from Anentropic's answer
class T(tuple):
    def __new__(cls, *args):
        return super().__new__(cls, tuple(sorted(args)))

d = {KEY: 4}
'''

funcs = [
    "tuple(sorted([s, t]))",
    "frozenset([s, t])",
    "frozenset((s, t))",
    "frozenset({s, t})",
    "key(s, t)",
    "(s, t) if s < t else (t, s)",
    "T(s, t)",
]

times = {f: [] for f in funcs}
def stats(f):
    ts = [t * 1e9 for t in sorted(times[f])[:10]]
    return f'{mean(ts):6.1f} ± {stdev(ts):3.1f} ns '

for _ in range(100):
    for f in funcs:
        code = f'for s, t in sts: d[{f}]'
        setup_ = setup.replace('KEY', f)
        t = timeit(code, setup_, number=10**0) / 1e4
        times[f].append(t)

for f in sorted(funcs, key=stats):
    print(stats(f), f)

Python相关问答推荐

如何销毁框架并使其在tkinter中看起来像以前的样子?

2维数组9x9,不使用numpy.数组(MutableSequence的子类)

处理(潜在)不断增长的任务队列的并行/并行方法

Pandas 有条件轮班操作

Vectorize多个头寸的止盈/止盈回溯测试pythonpandas

实现自定义QWidgets作为QTimeEdit的弹出窗口

如何在Polars中从列表中的所有 struct 中 Select 字段?

无法在Docker内部运行Python的Matlab SDK模块,但本地没有问题

不允许访问非IPM文件夹

使用Python从URL下载Excel文件

使用特定值作为引用替换数据框行上的值

如何使regex代码只适用于空的目标单元格

为什么常规操作不以其就地对应操作为基础?

根据Pandas中带条件的两个列的值创建新列

比较两个有条件的数据帧并删除所有不合格的数据帧

在round函数中使用列值

BeatuifulSoup从欧洲志愿者服务中获取数据和解析:一个从EU-Site收集机会的小铲子

如何在Pandas中用迭代器求一个序列的平均值?

与同步和异步客户端兼容的Python函数

Match-Case构造中的对象可调用性测试