Python 仅当尚未设置值时,才在dict中设置值

Python 仅当尚未设置值时,才在dict中设置值,python,dictionary,lazy-evaluation,Python,Dictionary,Lazy Evaluation,如果尚未设置值,那么在dict中设置值的最具python风格的方法是什么 目前,我的代码使用if语句: if "timeout" not in connection_settings: connection_settings["timeout"] = compute_default_timeout(connection_settings) dict.get(key,default)适用于使用dict的代码,而不适用于准备将dict传递给另一个函数的代码。你可以用它来设置一些东西,但在我

如果尚未设置值,那么在
dict
中设置值的最具python风格的方法是什么

目前,我的代码使用if语句:

if "timeout" not in connection_settings:
    connection_settings["timeout"] = compute_default_timeout(connection_settings)
dict.get(key,default)
适用于使用dict的代码,而不适用于准备将dict传递给另一个函数的代码。你可以用它来设置一些东西,但在我看来,它并不漂亮:

connection_settings["timeout"] = connection_settings.get("timeout", \
    compute_default_timeout(connection_settings))

即使dict包含密钥,也会评估计算函数;臭虫

Defaultdict是指默认值相同时

当然,有很多次您将不需要计算的primative值设置为默认值,它们当然可以使用
dict.setdefault
。但更复杂的情况如何?

一种方法是:

if key not in dict:
  dict[key] = value

这是一个有点不确定的答案,但我想说,最具python风格的是if语句。您抵制了使用
\uuuu setitem\uuuu
或其他方法对其进行线性化的冲动。您已经避免了逻辑中可能出现的错误,因为在尝试巧妙地进行短路
/
攻击时,可能会出现现有但错误的值。很明显,compute函数在没有必要的时候没有被使用

它清晰、简洁、易读——像蟒蛇一样

将精确地“仅当尚未设置值时才在dict中设置值”

您仍然需要计算值以将其作为参数传入:

connection_settings.setdefault("timeout", compute_default_timeout(connection_settings))

我使用以下命令将kwargs修改为非默认值并传递给另一个函数:

def f( **non_default_kwargs ):

    kwargs = {
        'a':1,
        'b':2,
    }
    kwargs.update( non_default_kwargs )

    f2( **kwargs )
这具有以下优点:

  • 你不必键入两次键

  • 所有这些都是在一个函数中完成的


    • 您可能需要
      dict.setdefault

      创建新词典并设置一个值:

      >>> d = {}
      >>> d.setdefault('timeout', 120)
      120
      >>> d
      {'timeout': 120}
      
      如果已设置值,
      dict.setdefault
      不会覆盖该值:

      >>> d['port']=8080
      >>> d.setdefault('port', 8888)
      8080
      >>> d
      {'port': 8080, 'timeout': 120}
      

      我发现,利用dict
      .get()
      方法的返回是
      None
      (Falsy),再加上
      ,在密钥不存在的情况下推迟对昂贵的网络请求的评估,既方便又明显

      d = dict()
      
      def fetch_and_set(d, key):
          d[key] = ("expensive operation to fetch key")
          if not d[key]:
              raise Exception("could not get value")
          return d[key]
      
      ...
      
      value = d.get(key) or fetch_and_set(d, key)
      

      具体地说,在我的例子中,我正在从缓存构建一个新的字典,然后在加速
      fn()
      调用之后更新缓存

      下面是我使用的简化视图

      j = load(database)  # dict
      d = dict()
      
      # see if desired keys are in the cache, else fetch
      for key in keys:
          d[key] = j.get(key) or fetch(key, network_token)
      
      fn(d)  # use d for something useful
      
      j.update(d)  # update database with new values (if any)
      

      由于Python3.9可以使用合并操作符
      |
      合并两个词典。以右边的格言为准:

      d={key:value}|d
      

      注意:这将创建一个具有更新值的新字典。

      “即使dict包含键,也会评估计算函数;bug”这不是bug:
      在调用
      dict.get()
      之前必须调用
      计算默认超时()
      ——这可能不是OP想要的,但这是正确的行为。引用自问题,因此很明显,该行为等同于OP已经考虑的某个bug(我自己不会称之为bug,但在这种情况下它仍然是不必要和不受欢迎的)。我的观点是,它显然对OP没有用处,因为他们已经因为这个原因放弃了一个同等的替代方案。。顺便说一句,我曾经问过一个关于这种行为的问题,是否可以让它变得“懒惰”…)我认为您对用例的假设太多了。OP可能没有(甚至不想要)一个预先存在和预先计算的合理默认值集合。如上所述,这样做是正确的。@98:但是,这并不能使
      dict.setdefault()
      成为正确的选择,因为它不是唯一的
      dict.setdefault()
      有。这与原始问题中显示的有什么不同吗?对未来的谷歌用户来说:这可能不是提问者想要的答案,但这是你想要的答案。@coredump错误我不同意。使用
      if key not in dict:dict[k]=value
      (如问题和接受的答案中所建议)更具可读性。
      dict.setdefault()
      设计用于在有效获取密钥值时,始终确保密钥存在。将其用作setter只会与正常使用背道而驰。如果关键字不在dictionary:dictionary[key]=compute\u default\u timeout(:…)
      中,则使用
      将更加清晰和直接。此外,OP特别要求提供一个直到需要时才计算新值的选项,这是您无法使用
      dict.setdefault()
      @MartijnPieters实现的,coredumperror已经提到这不是OP想要的答案。否则,避免重复查找(计算哈希等)是一个很好的解决方案。@Alexey:查找属性和调用方法也会有代价。由于字符串的散列值是缓存的,对于
      setdefault()
      您总是必须首先生成默认值,如果“超时”不在连接设置中,我会把钱放在
      上:连接设置['timeout']=compute\u default\u timeout(连接设置)
      获胜。但现在我们正在进行微优化,这将取决于关键点未命中率和命中率。我同意,现在使用python没有更好的方法,但我认为它有明显的缺点。特别是,您正在重复密钥的名称,因此拼写错误产生的bug已经成熟。因为它是一个字符串,所以如果在if语句中输入“timeout”,而在赋值中输入“timout”,大多数linter将无法捕获它。我在很大程度上不喜欢Ruby,但有一件事我很怀念| |=操作符。”连接设置[“timeout”]| |=计算_值()”简洁易读。顺便说一句,| |=远非完美。Ruby中该运算符的陷阱是如果值在字典中,但为falsy(0,空字符串等)