【问题标题】:index into Python dict with list to get a list, as with a Perl hash使用 list 索引 Python dict 以获取列表,就像使用 Perl 哈希一样
【发布时间】:2013-12-30 08:15:06
【问题描述】:

Perl 有一个构造(如 Joe Z 所指出的,称为“散列切片”)用于使用列表索引散列以获得列表,例如:

%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?我的 google-fu 找不到。

“它不是 Pythonic”:是的,我知道,但我希望它是。它简洁易读的,并且由于它永远不会是 Pythonic 来索引具有不可哈希类型的 dict,因此让异常处理程序为您迭代索引而不是 barfing 不会破坏大多数当前代码.

注意:正如 cmets 中所指出的,这个测试用例可以重写为一个列表,完全避免使用 dict,但我正在寻找一个通用的解决方案。

【问题讨论】:

  • 顺便说一句,上面使用的 Perl 功能的名称是“hash slice”。
  • 用列表代替字典怎么样?
  • 在这种情况下我可以,但这只是一个说明性的测试用例。
  • 我认为这样的事情会很好,但结果应该是什么?值或项目列表?一个子字典?我认为使用bleah.items([1,3])返回[('1','a'),('3','c')]bleah.values([1,3])返回['a', 'c']应该更好
  • 我不知道是否有任何答案考虑过这一点,但在 Perl 中,哈希切片是左值。例如,您可以像这样在哈希中分配键“a”和“c”:@hash{"a", "c"} = split "|", "foo|bar"

标签: python perl dictionary hash


【解决方案1】:

我能想到的最好方法是使用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 不错。但是 OP 想要传递一个列表,所以你可以使用 itemgetter(*[1,3]) 代替。 (或添加)
  • @koffein 谢谢 :) 更新了我的答案。
  • 更直接,你可以这样做print ' '.join(itemgetter(1,3)[bleah])
  • @thefourtheye 首先,[bleah] 在我的评论中是一个错字,意思是(bleah)。其次,不需要列表,但如果您已经将它们放在列表中(例如,变量而不是文字键),您可以使用 * 将其解压缩为 itemgetter 的单个参数。
  • 我试过了,itemgetter() 返回了一个元组。当然,编写 list(itemgetter(1, 3)(bleah)) 很容易,但这给我正在寻找的干净解决方案增加了一点麻烦。
【解决方案2】:

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

bleah[1:3]

【讨论】:

  • 不,因为它是一个字典,而不是一个列表。
  • 具有整数索引的 dict 应该很容易重构为列表。
  • Hyperboreus,是的,但我只是举一个简单的例子。
  • @jcomeau_ictx 在 perl 中 @bleah{1, 4} 是指“键 1、2、3、4”还是“键 1 和 4”?
  • 表示:键 1 和 4
【解决方案3】:

目前,我在 cmets 中采纳 Hyperboreus 的建议并覆盖 __getitem__,但我仍然认为将其设置为默认 dict 行为是有意义的:

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),另一方面 d[(1,3)]d[[1,3]] 会返回/表示不同的东西。我不确定这是否值得可能的混淆......
【解决方案4】:

我会说这是稍微更好,因为如果由于某种原因传递了未知键,则您有一个默认值,并且它避免了不必要的内部列表和导入(如@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 无法转换为字符串,这就是我在打印示例中传入空字符串的原因。

【讨论】:

  • 这可能对某些人有用。当我使用不存在的密钥时,我通常更喜欢引发异常。
猜你喜欢
  • 1970-01-01
  • 2017-11-11
  • 1970-01-01
  • 2011-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多