【问题标题】:Proper way to remove keys in dictionary with None values in Python在 Python 中使用 None 值删除字典中键的正确方法
【发布时间】:2016-02-21 04:42:30
【问题描述】:

在 Python 中从值 == None 的字典中删除键的正确方法是什么?

【问题讨论】:

  • 您必须澄清您的意思,因为字典中的每个键都必须有一个值,即使该值是 ''0None
  • {k: v for k, v in original.items() if v is not None}

标签: python dictionary


【解决方案1】:

通常,您将通过过滤旧的dict 来创建一个新的dict。字典推导非常适合这种事情:

{k: v for k, v in original.items() if v is not None}

如果你必须更新原来的dict,你可以这样做...

filtered = {k: v for k, v in original.items() if v is not None}
original.clear()
original.update(filtered)

这可能是我能想到的最“干净”的就地删除它们的方法(在迭代时修改 dict 是不安全的)


在python2.x上使用original.iteritems()

【讨论】:

  • 目前,此问题的其他两个答案似乎在从字典中删除时迭代字典。是你在最后一句中提到的吗?迭代 dict.keysdict.items?
  • @RobertL -- 是的。如果您要在此过程中更改字典,则迭代 dict 总是不安全的。在 python2.x 上迭代 dict.keys()dict.items() 很好,但在 python3.x 上不安全。例如,请参阅stackoverflow.com/a/6777632/748858
  • 感谢您使用 Python 3 语法进行更新。随着 Python 2 变老,这个答案变得越来越笨拙。也许是时候先提供 Python 3+ 的答案,并将遗留语法作为脚注。
  • @JasonR.Coombs -- 同意。我把这个改成默认为 python3.x —— 实际上这也可以在 python2.x 上工作,只是效率稍低。
  • 如果您有嵌套字典,这似乎不起作用
【解决方案2】:

如果您需要递归删除 None 值,最好使用这个:

def delete_none(_dict):
    """Delete None values recursively from all of the dictionaries"""
    for key, value in list(_dict.items()):
        if isinstance(value, dict):
            delete_none(value)
        elif value is None:
            del _dict[key]
        elif isinstance(value, list):
            for v_i in value:
                if isinstance(v_i, dict):
                    delete_none(v_i)

    return _dict

根据@dave-cz 的建议,添加了支持列表类型值的功能。

@mandragor 添加了额外的 if 语句以允许包含简单列表的字典。

如果您需要从字典、列表、元组、集合中删除所有 None 值,这也是一种解决方案:

def delete_none(_dict):
    """Delete None values recursively from all of the dictionaries, tuples, lists, sets"""
    if isinstance(_dict, dict):
        for key, value in list(_dict.items()):
            if isinstance(value, (list, dict, tuple, set)):
                _dict[key] = delete_none(value)
            elif value is None or key is None:
                del _dict[key]

    elif isinstance(_dict, (list, set, tuple)):
        _dict = type(_dict)(delete_none(item) for item in _dict if item is not None)

    return _dict


passed:
a = {
    "a": 12, "b": 34, "c": None,
    "k": {"d": 34, "t": None, "m": [{"k": 23, "t": None},[None, 1, 2, 3],{1, 2, None}], None: 123}
}

returned:
a = {
    "a": 12, "b": 34, 
    "k": {"d": 34, "m": [{"k": 23}, [1, 2, 3], {1, 2}]}
}

【讨论】:

  • 您需要在_dict.items() 之前添加list() 以避免由于dict 在迭代期间更改大小而引发RunTimeError
  • value 可以是具有 None 属性的字典列表,添加elif isinstance(value, list): for v_i in value: delete_none(v_i)
  • 不需要返回_dict。这只会增加运行时间。 _dict 通过引用方法传递。您可以将要从中删除的字典的副本传递给此函数并获取结果。
  • 如果您在顶层有一个数组,则缺少一个案例: mylist: [ 1, 2,3 ]
  • @Mandragor 好点,谢谢,一开始就不能检查,比如: if not isinstance(_dict, dict): return _dict but your looks good
【解决方案3】:

对于python 2.x

dict((k, v) for k, v in original.items() if v is not None)

【讨论】:

  • 我不能代表反对者,但这里的答案有一个问题是,如果 bool(v) 评估为 Falsenot v 将评估为 True。例如,v == ''(空字符串)就是这种情况,这与 v == None 不同。
  • @Rob is not 不等于 isnot 的组合。它是一个完全独立的运算符来测试身份。请参阅 operator.is_notfor Python2Python3
  • @AminShahGilani 是的,答案已被编辑。它以前使用if not v 而不是if v is not None。我现在应该删除我的评论吗?
【解决方案4】:

您还可以复制dict 以避免在更改原始dict 时对其进行迭代。

for k, v in dict(d).items():
    if v is None:
        del d[k]

但这对于较大的字典可能不是一个好主意。

【讨论】:

    【解决方案5】:

    如果你不想复制

    for k,v  in list(foo.items()):
       if v is None:
          del foo[k]
    

    【讨论】:

    • foo.pop(k) 更好,否则会失败
    • 仅供参考,list 关键字创建键/值对的(浅)副本。
    • foo.pop(k) 如果没有键也会失败,更好,pop(k, None)
    【解决方案6】:

    Python3递归版本

    def drop_nones_inplace(d: dict) -> dict:
        """Recursively drop Nones in dict d in-place and return original dict"""
        dd = drop_nones(d)
        d.clear()
        d.update(dd)
        return d
    
    def drop_nones(d: dict) -> dict:
        """Recursively drop Nones in dict d and return a new dict"""
        dd = {}
        for k, v in d.items():
            if isinstance(v, dict):
                dd[k] = drop_nones(v)
            elif isinstance(v, (list, set, tuple)):
                # note: Nones in lists are not dropped
                # simply add "if vv is not None" at the end if required
                dd[k] = type(v)(drop_nones(vv) if isinstance(vv, dict) else vv 
                                for vv in v) 
            elif v is not None:
                dd[k] = v
        return dd
    

    【讨论】:

      【解决方案7】:

      也许你会发现它很有用:

      def clear_dict(d):
          if d is None:
              return None
          elif isinstance(d, list):
              return list(filter(lambda x: x is not None, map(clear_dict, d)))
          elif not isinstance(d, dict):
              return d
          else:
              r = dict(
                      filter(lambda x: x[1] is not None,
                          map(lambda x: (x[0], clear_dict(x[1])),
                              d.items())))
              if not bool(r):
                  return None
              return r
      

      它会:

      clear_dict(
          {'a': 'b', 'c': {'d': [{'e': None}, {'f': 'g', 'h': None}]}}
      )
      
      ->
      
      {'a': 'b', 'c': {'d': [{'f': 'g'}]}}
      
      

      【讨论】:

        猜你喜欢
        • 2021-02-13
        • 1970-01-01
        • 1970-01-01
        • 2021-09-20
        • 2014-09-02
        • 2022-01-06
        • 2020-03-15
        • 1970-01-01
        • 2019-05-04
        相关资源
        最近更新 更多