使用列表作为Python字典的索引,获取列表,类似于Perl哈希。

6

Perl中有一种构造(称为“哈希片段”),可以使用列表对哈希进行索引,以获取列表,例如:

%bleah = (1 => 'a', 2 => 'b', 3 => 'c');
print join(' ', @bleah{1, 3}), "\n";

执行后返回:

a c

在Python中,我知道最简单、最易读的方法是使用列表推导式:

>>> bleah = {1: 'a', 2: 'b', 3: 'c'}
>>> print ' '.join([bleah[n] for n in [1, 3]])
a c

因为:

>>> bleah[[1, 2]]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

我是否遗漏了其他更好的方法?也许是在Python3中,我还没有使用过它?如果没有,是否有人知道是否已经提交了PEP?我的谷歌搜索无法找到任何信息。

"这不是Pythonic":是的,我知道,但我希望它是。它很简洁易读,并且由于使用不可哈希类型索引字典永远不会成为Pythonic,因此,异常处理程序为您迭代索引而不是抛出异常,不会破坏大多数当前代码。

注意:正如评论中指出的那样,这个测试用例可以重写为一个列表,完全避免使用字典,但我正在寻找一般解决方案。


3
顺便提一下,你上面使用的 Perl 功能名称是“哈希切片”。 - Joe Z
1
在这种情况下,我可以这样做,但这只是一个说明性的测试案例。 - jcomeau_ictx
我认为这样的东西会很不错,但结果应该是什么?是值或条目的列表还是子字典?我认为最好使用bleah.items([1,3])返回 [('1','a'),('3','c')] 或者使用 bleah.values([1,3]) 返回 ['a','c'] - koffein
@jcomeau_ictx 请检查我的答案。你可能想使用 itemgetter - thefourtheye
1
我不知道是否有任何答案考虑到这一点,但在Perl中,哈希切片是一个左值。因此,例如,您可以像这样为哈希中的键"a"和"c"赋值:@hash{"a", "c"} = split "|", "foo|bar" - tobyink
显示剩余5条评论
4个回答

4
我认为最好的方式是使用itemgetter
from operator import itemgetter
bleah = {1: 'a', 2: 'b', 3: 'c'}
getitems = itemgetter(1, 3)
print getitems(bleah)

所以,如果你想传递一个索引列表,那么你可以解包参数,像这样(感谢@koffein指出这一点:)
getitems = itemgetter(*[1, 3])

您可以像这样使用 join 来加入:
print ' '.join(getitems(bleah))

或者简单地说
print ' '.join(itemgetter(1, 3)(bleah))

1
+1 很好。但是原帖想要传递一个列表,所以你可以使用 itemgetter(*[1,3]) 代替(或添加它)。 - koffein
1
@koffein 谢谢 :) 我更新了我的回答。 - thefourtheye
更直接的做法是,你可以这么写:print ' '.join(itemgetter(1,3)[bleah]) - Mark Reed
@thefourtheye 首先,我评论中的 [bleah] 是打错了,应该是 (bleah)。其次,不需要一个列表,但如果你已经将它们放在一个列表中(例如一个变量而不是字面意义上的键),你可以使用 * 将其解包成单独的参数传递给 itemgetter - Mark Reed
我尝试了一下,itemgetter()返回了一个元组。当然,编写list(itemgetter(1, 3)(bleah))很容易,但这会给我寻找的简洁解决方案增加更多的杂质。 - jcomeau_ictx

1

如果您的字典具有连续的整数索引,则可以将其重建为列表并使用

bleah[1:3]

2
不可以,因为它是一个字典,而不是列表。 - JBernardo
一个包含整数索引的字典应该很容易重构为列表。 - Hyperboreus
Hyperboreus,是的,但我只是举了一个简单的例子。 - jcomeau_ictx
@jcomeau_ictx 在Perl中,@bleah{1, 4}是指“键1、2、3、4”还是“键1和4”? - Hyperboreus
它意味着:键1和4。 - jcomeau_ictx
@jcomeau_ictx 那么,亲爱的先生,我的回答是胡说八道。你可以重写 __getitem__(py3)并传递元组(与列表不同,元组是可哈希的:a[[1,2]] 不起作用,但 a[1,2]a[(1,2)] 可以)。Numpy 经常这样做。 - Hyperboreus

1

目前,我正在采用评论中Hyperboreus的建议,并覆盖__getitem__,但我仍然认为将其作为默认字典行为是有意义的:

jcomeau@aspire:~$ cat /tmp/sliceable.py; echo ---; python /tmp/sliceable.py
'SliceableDict test'
import sys, os
class SliceableDict(dict):
 def __init__(self, d = {}):
  super(SliceableDict, self).__init__(d)
 def __getitem__(self, index):
  try:
   return super(SliceableDict, self).__getitem__(index)
  except TypeError:
   return [super(SliceableDict, self).__getitem__(x) for x in index]
 def __setitem__(self, index, value):
  try:
   super(SliceableDict, self).__setitem__(index, value)
  except:
   for i in range(len(index)):
    super(SliceableDict, self).__setitem__(index[i], value[i])
d = SliceableDict({1: 'a', 2: 'b', 3: 'c'})
print d[2]
print d[[1, 3]]
d[[1, 3]] = ['x', 'y']
print d
---
b
['a', 'c']
{1: 'x', 2: 'b', 3: 'y'}

(根据tobyink在上面的评论,进行修改以添加左值能力 -- jc 2013-12-12)

1
一方面,我认为这是个有用且不错的想法(+1),另一方面,d[(1,3)]d[[1,3]] 会返回/表示不同的事情。我不确定是否值得冒可能存在的混淆之险... - koffein

0

我认为这个稍微好一些,因为如果出现未知的键传递,它有一个默认值,并且避免了不必要的内部列表和导入(就像@thefourtheye的示例):

bleah = {1: 'a', 2: 'b', 3: 'c'}
print(' '.join(bleah.get(i, '') for i in (1, 3)))

如果您只需要返回值列表:
list(bleah.get(i) for i in (1, 3))

这里不需要默认值,因为get的默认值是NoneNone无法转换为字符串,这就是为什么在打印示例中传入了一个空字符串。


这可能对某些人有用。当我使用不存在的键时,我通常更喜欢引发异常。 - jcomeau_ictx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接