映射python字典中的值


243

给定一个字典,{ k1: v1, k2: v2 ... }我想{ k1: f(v1), k2: f(v2) ... }提供一个函数f

有没有这样的内置功能?还是我必须做

dict([(k, f(v)) for (k, v) in my_dictionary.iteritems()])

理想情况下,我只会写

my_dictionary.map_values(f)

要么

my_dictionary.mutate_values_with(f)

也就是说,对原始词典进行了突变还是创建副本对我来说都没有关系。


2
编写示例的更好方法是dict((k, f(v)) for k, v in mydict.iteritems()),即没有方括号,这将阻止通过生成器创建中间列表。
bereal

Answers:


354

没有这样的功能;最简单的方法是使用dict理解:

my_dictionary = {k: f(v) for k, v in my_dictionary.items()}

在python 2.7中,请使用.iteritems()方法而不是.items()节省内存。dict理解语法直到python 2.7才引入。

注意,列表上也没有这种方法。您将不得不使用列表推导或map()函数。

这样,您也可以使用该map()函数来处理字典:

my_dictionary = dict(map(lambda kv: (kv[0], f(kv[1])), my_dictionary.iteritems()))

但这确实不是那么可读。


5
+1:这也是我要做的。 dict(zip(a, map(f, a.values())))稍微短一点,但是我必须考虑一下它在做什么,并提醒自己,如果dict不变,则键和值将以相同的顺序进行迭代。我完全不必考虑dictcomp在做什么,因此这是正确的答案。
DSM

2
@chiborg:这是因为您现在不使用一次性查找所有键值对,而是使用键数乘以my_dictionary.__getitem__调用。
的Martijn Pieters的

1
注意,由于PEP3113(在python 3.x中实现)不再支持元组参数:lambda (k,v): (k, f(v))将被重写为lambda k_v: (k_v[0], f(k_v[1]))
normanius

1
为什么参数拆包变得无法解决?那有什么改善呢?
javadba

3
来自FP语言的Python似乎非常笨拙。
juanchito


21

您可以就地执行此操作,而不是创建一个新的字典,这对于大型词典(如果您不需要副本)可能更可取。

def mutate_dict(f,d):
    for k, v in d.iteritems():
        d[k] = f(v)

my_dictionary = {'a':1, 'b':2}
mutate_dict(lambda x: x+1, my_dictionary)

结果my_dictionary包含:

{'a': 2, 'b': 3}

1
太酷了,您可能应该重命名mapdict为,mutate_values_with或用某种方式使其清楚地重写了dict!:)
Tarrasch 2014年

2
zip(d.keys(), d.values())适用于更多版本,而不是iteritems()
ytpillai

1
@ytpillai'zip'或理解力是一个副本,而不是就地更改值,这是我回答的目的。如果可以,则可接受的答案是最佳答案。
gens 2015年

1
抱歉,我没有意识到您要使用items方法。但是,对于非Python 2.7用户,也可以对此进行进一步的改进{k:f(v) for k,v in iter(d.items())}
ytpillai 2015年

1
通过制作迭代器来节省空间
ytpillai 2015年


4

虽然我的原始答案没有指出要点(通过尝试使用defaultdict的工厂中的Accessing key解决方案来解决此问题),但我对其进行了重新设计以提出针对当前问题的实际解决方案。

这里是:

class walkableDict(dict):
  def walk(self, callback):
    try:
      for key in self:
        self[key] = callback(self[key])
    except TypeError:
      return False
    return True

用法:

>>> d = walkableDict({ k1: v1, k2: v2 ... })
>>> d.walk(f)

想法是将原始dict子类化以赋予其所需的功能:在所有值上“映射”一个功能。

加号的是,该字典可用于存储原始数据,就好像它是一个dict,同时根据请求通过回调转换任何数据。

当然,可以随意使用所需的名称来命名类和函数(此答案中选择的名称受PHP array_walk()函数的启发)。

注意:try- except块和return语句都不是功能必需的,它们可以进一步模仿PHP的行为array_walk


1
由于无法为__missing__要转换的现有键调用该方法,因此无法解决OP问题,除非通过的工厂方法以某种方式将origin dict用作后备,但这不是示例用法的一部分,我认为这对眼前的问题没有令人满意的答案。
Kaos

现有哪些密钥?
7heo.tk's

在OP中:Given a dictionary { k1: v1, k2: v2 ... } ...。也就是说,您已经有一个dict开始
。– Kaos

我想说我们都是对的。但我相信我们俩都是错的。你是对的,因为我的回答没有回答问题;但不是出于您调用的原因。我只是错过了要点,提供了一种获得{v1: f(v1), v2: f(v2), ...}给定的方式[v1, v2, ...],而不是给定一个命令的方式。我将编辑我的答案以纠正该问题。
7heo.tk's

2

为了避免从lambda内部进行索引,例如:

rval = dict(map(lambda kv : (kv[0], ' '.join(kv[1])), rval.iteritems()))

您也可以这样做:

rval = dict(map(lambda(k,v) : (k, ' '.join(v)), rval.iteritems()))

在第二个示例中,这是2元组本身的巧妙操纵。但是,它利用了lambda中的自动元组拆包功能,Python 3不再支持该功能。因此lambda(k,v)无法正常工作。参见stackoverflow.com/questions/21892989/…–
乔纳森·科玛

0

刚遇到这个用例。我实现了gens的answer,添加了一种递归方法来处理也是dict的值:

def mutate_dict_in_place(f, d):
    for k, v in d.iteritems():
        if isinstance(v, dict):
            mutate_dict_in_place(f, v)
        else:
            d[k] = f(v)

# Exemple handy usage
def utf8_everywhere(d):
    mutate_dict_in_place((
        lambda value:
            value.decode('utf-8')
            if isinstance(value, bytes)
            else value
        ),
        d
    )

my_dict = {'a': b'byte1', 'b': {'c': b'byte2', 'd': b'byte3'}}
utf8_everywhere(my_dict)
print(my_dict)

这在处理在Python 2中将字符串编码为字节的json或yaml文件时非常有用

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.