代码之家  ›  专栏  ›  技术社区  ›  Eli Bendersky

“setdefault”dict方法的用例

  •  171
  • Eli Bendersky  · 技术社区  · 14 年前

    添加 collections.defaultdict 在python2.5中大大减少了 dict setdefault 方法。这个问题是针对我们的集体教育:

    1. 设置默认值 今天在python2.6/2.7中仍然有用吗?
    2. 什么流行的用例 设置默认值 集合.defaultdict
    16 回复  |  直到 14 年前
        1
  •  221
  •   Stan James nfaggian    4 年前

    你可以说 defaultdict 用于设置默认值 在填字典之前 setdefault 用于设置默认值 填完口述时或之后 .

    可能是最常见的用例:分组项(在未排序的数据中,否则使用 itertools.groupby

    # really verbose
    new = {}
    for (key, value) in data:
        if key in new:
            new[key].append( value )
        else:
            new[key] = [value]
    
    
    # easy with setdefault
    new = {}
    for (key, value) in data:
        group = new.setdefault(key, []) # key might exist already
        group.append( value )
    
    
    # even simpler with defaultdict 
    from collections import defaultdict
    new = defaultdict(list)
    for (key, value) in data:
        new[key].append( value ) # all keys have a default already
    

    有时您需要确保在创建dict之后存在特定的键。 默认dict 在这种情况下不起作用,因为它只在显式访问上创建键。假设您使用了带有许多标头的HTTP-ish—有些是可选的,但您希望它们具有默认值:

    headers = parse_headers( msg ) # parse the message, get a dict
    # now add all the optional headers
    for headername, defaultvalue in optional_headers:
        headers.setdefault( headername, defaultvalue )
    
        2
  •  33
  •   Matt Joiner    14 年前

    setdefault

    def notify(self, level, *pargs, **kwargs):
        kwargs.setdefault("persist", level >= DANGER)
        self.__defcon.set(level, **kwargs)
        try:
            kwargs.setdefault("name", self.client.player_entity().name)
        except pytibia.PlayerEntityNotFound:
            pass
        return _notify(level, *pargs, **kwargs)
    

    它非常适合在包装器中围绕接受关键字参数的函数调整参数。

        3
  •  17
  •   David Kanarek    13 年前

    defaultdict

    例如,我需要一个字典来将字符串映射到唯一的int。 defaultdict(int) 将始终使用0作为默认值。同样地, defaultdict(intGen())

    相反,我用了一个普通的口述:

    nextID = intGen()
    myDict = {}
    for lots of complicated stuff:
        #stuff that generates unpredictable, possibly already seen str
        strID = myDict.setdefault(myStr, nextID())
    

    请注意 dict.get(key, nextID()) 是不够的,因为我以后也需要能够引用这些值。

    intGen

    class intGen:
        def __init__(self):
            self.i = 0
    
        def __call__(self):
            self.i += 1
        return self.i
    

    如果有人有办法 默认dict

        4
  •  13
  •   picmate 涠   4 年前

    正如大多数答案所说 setdefault defaultdict 允许您在键不存在时设置默认值。然而,我想指出一个关于 设置默认值 . 当Python解释器执行时 设置默认值 它将始终对函数的第二个参数求值,即使该键存在于字典中。例如:

    In: d = {1:5, 2:6}
    
    In: d
    Out: {1: 5, 2: 6}
    
    In: d.setdefault(2, 0)
    Out: 6
    
    In: d.setdefault(2, print('test'))
    test
    Out: 6
    

    如你所见, print 设置默认值 memoization . 如果将递归函数调用作为第二个参数添加到 设置默认值 ,您不会从中获得任何性能,因为Python总是递归地调用函数。

    既然提到了回忆录,一个更好的选择就是使用functools.lru\u缓存如果你考虑用记忆来增强一个函数的话。lru\u cache可以更好地处理递归函数的缓存需求。

        5
  •  10
  •   Community Egal    7 年前

    我用 setdefault() 当我想在 OrderedDict . 没有一个标准的Python集合可以同时实现这两种功能,但是 are ways 实现这样一个集合。

        6
  •  8
  •   David Kanarek    13 年前

    正如穆罕默德所说,在某些情况下,您有时只希望设置一个默认值。一个很好的例子是数据结构,它首先被填充,然后被查询。

    考虑一个trie。添加字时,如果需要子节点但不存在,则必须创建子节点以扩展trie。查询是否存在某个单词时,缺少的子节点表示该单词不存在,因此不应创建该单词。

    defaultdict不能这样做。相反,必须使用带有get和setdefault方法的常规dict。

        7
  •  5
  •   Muhammad Alkarouri    14 年前

    setdefault 如果你 有时 想设置一个默认值,有时不。在现实生活中,我还没有遇到这样的用例。

    >>> mydata = local()
    >>> mydata.__dict__
    {'number': 42}
    >>> mydata.__dict__.setdefault('widgets', [])
    []
    >>> mydata.widgets
    []
    

    我要说的是 __dict__.setdefault

    :碰巧,这是标准库中唯一的示例,它位于注释中。因此,仅仅一个案例还不足以证明 设置默认值 . 不过,这里有一个解释:

    对象将其属性存储在 __dict__ 属性。碰巧的是 属性在创建对象后的任何时候都是可写的。它也是一本字典而不是一本书 defaultdict . 一般情况下,物体具有 作为一个 默认dict __dict\设置默认值 ,如果认为没有用,则完全删除。

        8
  •  3
  •   Wizard    6 年前

    我改写了公认的答案,并使之更容易为新手所接受。

    #break it down and understand it intuitively.
    new = {}
    for (key, value) in data:
        if key not in new:
            new[key] = [] # this is core of setdefault equals to new.setdefault(key, [])
            new[key].append(value)
        else:
            new[key].append(value)
    
    
    # easy with setdefault
    new = {}
    for (key, value) in data:
        group = new.setdefault(key, []) # it is new[key] = []
        group.append(value)
    
    
    
    # even simpler with defaultdict
    new = defaultdict(list)
    for (key, value) in data:
        new[key].append(value) # all keys have a default value of empty list []
    

    此外,我将这些方法分类为参考:

    dict_methods_11 = {
                'views':['keys', 'values', 'items'],
                'add':['update','setdefault'],
                'remove':['pop', 'popitem','clear'],
                'retrieve':['get',],
                'copy':['copy','fromkeys'],}
    
        9
  •  3
  •   xged    6 年前

    defaultdict 结束 dict ( dict.setdefault )那是一辆汽车吗 默认dict 对象创建新项 每次 给出了不存在的密钥(如 == , print 类通常比 口述 类,则更难将其序列化。

        10
  •  2
  •   Stefan Gruenwald    10 年前

    下面是setdefault的一些示例,以显示其有用性:

    """
    d = {}
    # To add a key->value pair, do the following:
    d.setdefault(key, []).append(value)
    
    # To retrieve a list of the values for a key
    list_of_values = d[key]
    
    # To remove a key->value pair is still easy, if
    # you don't mind leaving empty lists behind when
    # the last value for a given key is removed:
    d[key].remove(value)
    
    # Despite the empty lists, it's still possible to 
    # test for the existance of values easily:
    if d.has_key(key) and d[key]:
        pass # d has some values for key
    
    # Note: Each value can exist multiple times!
    """
    e = {}
    print e
    e.setdefault('Cars', []).append('Toyota')
    print e
    e.setdefault('Motorcycles', []).append('Yamaha')
    print e
    e.setdefault('Airplanes', []).append('Boeing')
    print e
    e.setdefault('Cars', []).append('Honda')
    print e
    e.setdefault('Cars', []).append('BMW')
    print e
    e.setdefault('Cars', []).append('Toyota')
    print e
    
    # NOTE: now e['Cars'] == ['Toyota', 'Honda', 'BMW', 'Toyota']
    e['Cars'].remove('Toyota')
    print e
    # NOTE: it's still true that ('Toyota' in e['Cars'])
    
        11
  •  1
  •   woodm1979    9 年前

    我经常使用setdefault当,得到这个,设置一个默认值(!!!)在字典里;有点常见操作系统环境词典:

    # Set the venv dir if it isn't already overridden:
    os.environ.setdefault('VENV_DIR', '/my/default/path')
    

    # Set the venv dir if it isn't already overridden:
    if 'VENV_DIR' not in os.environ:
        os.environ['VENV_DIR'] = '/my/default/path')
    

    值得注意的是,还可以使用生成的变量:

    venv_dir = os.environ.setdefault('VENV_DIR', '/my/default/path')
    

    但这并没有违约前那么必要。

        12
  •  1
  •   Tuttle    8 年前

    有时,您会根据对象的id保留对象的缓存dict,其中主实例位于缓存中,并且希望在缺少时设置缓存。

    return self.objects_by_id.setdefault(obj.id, obj)
    

    当您总是希望每个不同的id都保留一个实例时,无论每次如何获取obj,这都非常有用。例如,当对象属性在内存中得到更新,而保存到存储被推迟。

        13
  •  1
  •   Ethan Furman    7 年前

    我偶然发现了一个非常重要的用例: dict.setdefault() 当您只需要一个规范对象(而不是恰好相等的多个对象)时,它非常适合多线程代码。

    例如 (Int)Flag Enum in Python 3.6.0 has a bug :如果多个线程正在竞争一个组合 (Int)标志 成员,可能会有不止一个:

    from enum import IntFlag, auto
    import threading
    
    class TestFlag(IntFlag):
        one = auto()
        two = auto()
        three = auto()
        four = auto()
        five = auto()
        six = auto()
        seven = auto()
        eight = auto()
    
        def __eq__(self, other):
            return self is other
    
        def __hash__(self):
            return hash(self.value)
    
    seen = set()
    
    class cycle_enum(threading.Thread):
        def run(self):
            for i in range(256):
                seen.add(TestFlag(i))
    
    threads = []
    for i in range(8):
        threads.append(cycle_enum())
    
    for t in threads:
        t.start()
    
    for t in threads:
        t.join()
    
    len(seen)
    # 272  (should be 256)
    

    解决方法是使用 setdefault() 作为保存计算的复合成员的最后一步——如果已经保存了另一个成员,则使用它来代替新成员,从而保证枚举成员的唯一性。

        14
  •  0
  •   YvesgereY    8 年前

    大错特错!

    扩展塔特尔的答案。对我来说,最好的用例是缓存机制。而不是:

    if x not in memo:
       memo[x]=long_computation(x)
    return memo[x]
    

    消耗3行和2或3次查找, 我很乐意写信

    return memo.setdefault(x, long_computation(x))
    
        15
  •  0
  •   Fred    7 年前

    http://stupidpythonideas.blogspot.com/2013/08/defaultdict-vs-setdefault.html

    简言之,决策(在非性能关键型应用程序中)应基于您希望如何处理空键的查找( 即。 KeyError 相对于默认值)。

        16
  •  0
  •   Iodnas    7 年前

    setdefault() 当你不想覆盖的时候 defaultdict 覆盖,而 没有。对于嵌套字典,更常见的情况是,您只希望在尚未设置键时设置默认值,因为您不希望删除当前的子字典。这是你使用 设置默认值()

    示例 默认dict :

    >>> from collection import defaultdict()
    >>> foo = defaultdict()
    >>> foo['a'] = 4
    >>> foo['a'] = 2
    >>> print(foo)
    defaultdict(None, {'a': 2})
    

    setdefault 不覆盖:

    >>> bar = dict()
    >>> bar.setdefault('a', 4)
    >>> bar.setdefault('a', 2)
    >>> print(bar)
    {'a': 4}
    
        17
  •  0
  •   0xack13    3 年前

    除了建议之外, setdefault duplicate 已设置的键,则不会更新该键的值。您将保留第一个遇到的值。就好像只迭代/更新一次重复的键一样。

    下面是记录已排序列表的键/元素索引的代码示例:

    nums = [2,2,2,2,2]
    d = {}
    for idx, num in enumerate(sorted(nums)):
        # This will be updated with the value/index of the of the last repeated key
        # d[num] = idx # Result (sorted_indices): [4, 4, 4, 4, 4]
        # In the case of setdefault, all encountered repeated keys won't update the key.
        # However, only the first encountered key's index will be set 
        d.setdefault(num,idx) # Result (sorted_indices): [0, 0, 0, 0, 0]
    
    sorted_indices = [d[i] for i in nums]