用空字符串从 dict 中移除键的有效方法

我有一个 dict,并希望删除所有键,其中有空值字符串。

metadata = {u'Composite:PreviewImage': u'(Binary data 101973 bytes)',
u'EXIF:CFAPattern2': u''}

最好的方法是什么?

158580 次浏览

巨蟒2 X

dict((k, v) for k, v in metadata.iteritems() if v)

Python 2.7-3. X

{k: v for k, v in metadata.items() if v}

注意,所有键都有值。只是其中一些值是空字符串。没有一个键是没有值的; 如果它没有值,它就不会出现在 dict 中。

布伦巴恩的解决方案 是理想的(我可以加上 pythonic) ,但是这里有另一个(fp)解决方案:

from operator import itemgetter
dict(filter(itemgetter(1), metadata.items()))

如果你真的需要修改原来的字典:

empty_keys = [k for k,v in metadata.iteritems() if not v]
for k in empty_keys:
del metadata[k]

注意,我们必须创建一个空键列表,因为在迭代时我们不能修改字典(您可能已经注意到了)。不过,这比创建一个全新的字典要便宜(在内存方面) ,除非有很多条目具有空值。

它甚至可以比 布伦巴恩的解决方案更短(而且我认为更易读)

{k: v for k, v in metadata.items() if v}

使用 Python 2.7.3进行测试。

如果你有一个嵌套的字典,并且你希望它甚至对空的子元素也能工作,你可以使用 BrenBarn 建议的一个递归变体:

def scrub_dict(d):
if type(d) is dict:
return dict((k, scrub_dict(v)) for k, v in d.iteritems() if v and scrub_dict(v))
else:
return d

基于 Ryan 的解决方案,如果你也有列表和嵌套字典:

对于 Python 2:

def remove_empty_from_dict(d):
if type(d) is dict:
return dict((k, remove_empty_from_dict(v)) for k, v in d.iteritems() if v and remove_empty_from_dict(v))
elif type(d) is list:
return [remove_empty_from_dict(v) for v in d if v and remove_empty_from_dict(v)]
else:
return d

对于 Python 3:

def remove_empty_from_dict(d):
if type(d) is dict:
return dict((k, remove_empty_from_dict(v)) for k, v in d.items() if v and remove_empty_from_dict(v))
elif type(d) is list:
return [remove_empty_from_dict(v) for v in d if v and remove_empty_from_dict(v)]
else:
return d

快速回答(TL; DR)

例子01

### example01 -------------------


mydict  =   { "alpha":0,
"bravo":"0",
"charlie":"three",
"delta":[],
"echo":False,
"foxy":"False",
"golf":"",
"hotel":"   ",
}
newdict =   dict([(vkey, vdata) for vkey, vdata in mydict.iteritems() if(vdata) ])
print newdict


### result01 -------------------
result01 ='''
{'foxy': 'False', 'charlie': 'three', 'bravo': '0'}
'''

详细答案

问题

  • 背景: Python 2. x
  • 场景: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    • 也就是从字典中删除空值
    • 也就是删除具有空白值的键
    • 又名过滤字典,用于每个键值对上的非空值

解决方案

  • Example01使用带简单条件的 python 列表理解语法来删除“空”值

陷阱

  • Example01只对原始字典的一个副本进行操作(不在适当的位置进行修改)
  • Example01可能会产生意想不到的结果,这取决于开发人员所说的“空”是什么意思
    • 开发人员是否意味着保留 假的值?
    • 如果字典中的值不能保证是字符串,开发人员可能会出现意外的数据丢失。
    • Results t01显示只保留了原始集合中的三个键-值对

另一个例子

  • Example02有助于处理潜在的陷阱
  • 方法是通过更改条件来使用更精确的“空”定义。
  • 在这里,我们只想筛选出计算结果为空字符串的值。
  • 在这里,我们还使用. Strip ()来过滤出只包含空格的值。

例子02

### example02 -------------------


mydict  =   { "alpha":0,
"bravo":"0",
"charlie":"three",
"delta":[],
"echo":False,
"foxy":"False",
"golf":"",
"hotel":"   ",
}
newdict =   dict([(vkey, vdata) for vkey, vdata in mydict.iteritems() if(str(vdata).strip()) ])
print newdict


### result02 -------------------
result02 ='''
{'alpha': 0,
'bravo': '0',
'charlie': 'three',
'delta': [],
'echo': False,
'foxy': 'False'
}
'''

参见

Patricasz新尼奥州的答案为基础,考虑到你可能想删除那些只包含某些虚假内容(如 '')而不包含其他内容(如 0)的键,或者你甚至可能想包含一些真实内容(如 'SPAM') ,然后你可以制作一个高度具体的击杀名单:

unwanted = ['', u'', None, False, [], 'SPAM']

不幸的是,这并不能很好地工作,因为例如,0 in unwanted的计算结果是 True。我们需要区分 0和其他虚假的东西,所以我们必须使用 is:

any([0 is i for i in unwanted])

评估为 False

现在用它来处理 del中不需要的东西:

unwanted_keys = [k for k, v in metadata.items() if any([v is i for i in unwanted])]
for k in unwanted_keys: del metadata[k]

如果你想要一个新的字典,而不是修改 metadata:

newdict = {k: v for k, v in metadata.items() if not any([v is i for i in unwanted])}

如果您想要一种功能全面、简洁的方法来处理通常是嵌套的、甚至可以包含循环的实际数据结构,我建议您查看 从 Boltons 实用程序包重新映射实用程序

在将 pip install boltonsIterutils.py复制到您的项目中之后,只需执行以下操作:

from boltons.iterutils import remap


drop_falsey = lambda path, key, value: bool(value)
clean = remap(metadata, visit=drop_falsey)

这个页面 有更多的例子,包括使用 Github 的 API 中更大的对象的例子。

它是纯 Python 的,所以它在任何地方都可以工作,并且在 Python 2.7和3.3 + 中进行了充分的测试。最棒的是,我就是为这样的案子写的,所以如果你发现它不能处理的案子,你可以窃听我修复它 就在这里

巨蟒3

dict((k, v) for k, v in metadata.items() if v)

一些基准:

1. 列表内涵重建字典

In [7]: %%timeit dic = {str(i):i for i in xrange(10)}; dic['10'] = None; dic['5'] = None
...: dic = {k: v for k, v in dic.items() if v is not None}
1000000 loops, best of 7: 375 ns per loop

2. 列表内涵使用 dict ()重新创建游戏词典

In [8]: %%timeit dic = {str(i):i for i in xrange(10)}; dic['10'] = None; dic['5'] = None
...: dic = dict((k, v) for k, v in dic.items() if v is not None)
1000000 loops, best of 7: 681 ns per loop

3. 如果 v 为无,则循环并删除键

In [10]: %%timeit dic = {str(i):i for i in xrange(10)}; dic['10'] = None; dic['5'] = None
...: for k, v in dic.items():
...:   if v is None:
...:     del dic[k]
...:
10000000 loops, best of 7: 160 ns per loop

所以循环和删除在160ns 时是最快的,在 ~ 375ns 时列表内涵是慢的一半,在调用 dict()时又是 ~ 680ns 时慢的一半。

将3包装到一个函数中会使它再次降低到275ns 左右。对我来说,PyPy 的速度是 neet python 的两倍。

另一种方法是使用字典理解。这应该与 2.7+兼容

result = {
key: value for key, value in
{"foo": "bar", "lorem": None}.items()
if value
}

我读了这个帖子里的所有回复,有些还提到了这个帖子: 使用递归函数删除嵌套字典中的空字符

我最初在这里使用的解决方案,它工作得很好:

尝试1: 太热(不能执行或不能防止未来发生) :

def scrub_dict(d):
if type(d) is dict:
return dict((k, scrub_dict(v)) for k, v in d.iteritems() if v and scrub_dict(v))
else:
return d

但是,在 Python 2.7的世界中,出现了一些性能和兼容性方面的问题:

  1. 使用 isinstance代替 type
  2. 为了提高效率,将列表编码展开到 for循环中
  3. 使用 python3安全的 items代替 iteritems

尝试2: 太冷(缺乏记忆) :

def scrub_dict(d):
new_dict = {}
for k, v in d.items():
if isinstance(v,dict):
v = scrub_dict(v)
if not v in (u'', None, {}):
new_dict[k] = v
return new_dict

DOH! 这不是递归的,根本不是回忆。

尝试3: 恰到好处(到目前为止) :

def scrub_dict(d):
new_dict = {}
for k, v in d.items():
if isinstance(v,dict):
v = scrub_dict(v)
if not v in (u'', None, {}):
new_dict[k] = v
return new_dict

如果你正在使用 pandas,这里有一个选项:

import pandas as pd


d = dict.fromkeys(['a', 'b', 'c', 'd'])
d['b'] = 'not null'
d['c'] = ''  # empty string


print(d)


# convert `dict` to `Series` and replace any blank strings with `None`;
# use the `.dropna()` method and
# then convert back to a `dict`
d_ = pd.Series(d).replace('', None).dropna().to_dict()


print(d_)

上面提到的一些方法忽略任何整数,并使用值为0.0.0的 float

如果有人想避免上述情况,可以使用下面的代码(从嵌套字典和嵌套列表中删除空字符串和 Nothing 值) :

def remove_empty_from_dict(d):
if type(d) is dict:
_temp = {}
for k,v in d.items():
if v == None or v == "":
pass
elif type(v) is int or type(v) is float:
_temp[k] = remove_empty_from_dict(v)
elif (v or remove_empty_from_dict(v)):
_temp[k] = remove_empty_from_dict(v)
return _temp
elif type(d) is list:
return [remove_empty_from_dict(v) for v in d if( (str(v).strip() or str(remove_empty_from_dict(v)).strip()) and (v != None or remove_empty_from_dict(v) != None))]
else:
return d

与数组混合的字节

  • 来自 BlissRage 的回答尝试3: 恰到好处(到目前为止)处的答案不能正确处理数组元素。我包括一个补丁,以防有人需要它。该方法是带有 if isinstance(v, list):语句块的句柄列表,它使用原始的 scrub_dict(d)实现擦除列表。
    @staticmethod
def scrub_dict(d):
new_dict = {}
for k, v in d.items():
if isinstance(v, dict):
v = scrub_dict(v)
if isinstance(v, list):
v = scrub_list(v)
if not v in (u'', None, {}, []):
new_dict[k] = v
return new_dict


@staticmethod
def scrub_list(d):
scrubbed_list = []
for i in d:
if isinstance(i, dict):
i = scrub_dict(i)
scrubbed_list.append(i)
return scrubbed_list

正如我目前为我的 Python 工作编写的桌面应用程序,我发现在数据输入应用程序中有很多条目,其中一些不是强制性的,因此用户可以留空,为了验证目的,很容易获取所有条目,然后丢弃空键或字典值。所以我上面的代码展示了我们如何轻松地把它们取出来,使用字典理解并保留字典值元素中不是空的。我使用 Python 3.8.3

data = {'':'', '20':'', '50':'', '100':'1.1', '200':'1.2'}


dic = {key:value for key,value in data.items() if value != ''}


print(dic)


{'100': '1.1', '200': '1.2'}

要保留0和 False 值,但去掉可以使用的空值:

{k: v for k, v in metadata.items() if v or v == 0 or v is False}

对于具有混合类型值的嵌套结果,您可以使用:

def remove_empty_from_dict(d):
if isinstance(d, dict):
return dict((k, remove_empty_from_dict(v)) for k, v in d.items() \
if v or v == 0 or v is False and remove_empty_from_dict(v) is not None)
elif isinstance(d, list):
return [remove_empty_from_dict(v) for v in d
if v or v == 0 or v is False and remove_empty_from_dict(v) is not None]
else:
if d or d == 0 or d is False:
return d
metadata ={'src':'1921','dest':'1337','email':'','movile':''}
ot = {k: v for k, v in metadata.items() if v != ''}
print(f"Final {ot}")

您还可以选择 filter方法:

filtered_metadata = dict( filter(lambda val: val[1] != u'', metadata.items()) )