Варианты использования 'значение setdefault метод дикт



дополнение collections.defaultdict в Python 2.5 значительно уменьшена потребность в dict ' s setdefault метод. Этот вопрос для нашего коллективного образования:




  1. что это setdefault еще полезно, сегодня в Python 2.6/2.7?

  2. какие популярные случаи использования setdefault были заменены на collections.defaultdict?

541   16  

16 ответов:

можно сказать 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 
new = defaultdict(list)
for (key, value) in data:
    new[key].append( value ) # all keys have a default already

иногда вы хотите убедиться, что определенные ключи существуют после создания dict. defaultdict не работает в этом случае, потому что он только создает ключи при явном доступе. Думаю, вы используете что-то 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 )

Я обычно использую 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)

это отлично подходит для настройки аргументов в оболочках вокруг функций, которые принимают аргументы ключевых слов.

defaultdict отлично, когда значение по умолчанию является статическим, как новый список, но не так много, если он динамический.

например, мне нужен словарь для сопоставления строк с уникальными ints. defaultdict(int) всегда будет использовать 0 в качестве значения по умолчанию. Аналогично,defaultdict(intGen()) всегда производит 1.

вместо этого я использовал обычный дикт:

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 крошечный класс I строит, что автоматически увеличивает int и возвращает его значение:

class intGen:
    def __init__(self):
        self.i = 0

    def __call__(self):
        self.i += 1
    return self.i

если у кого-то есть способ сделать это с defaultdict Я хотел бы увидеть это.

Я использую setdefault() когда я хочу значение по умолчанию в OrderedDict. Нет стандартной коллекции Python, которая делает и то, и другое, но есть areспособы для реализации такой коллекции.

как Мухаммад сказал, Есть ситуации, в которых вы только иногда хотите установить значение по умолчанию. Отличным примером этого является структура данных, которая сначала заполняется, а затем запрашивается.

рассмотрим три. При добавлении слова, если подузел необходим, но отсутствует, он должен быть создан для расширения trie. При запросе на наличие слова отсутствующий подузел указывает, что слово отсутствует и его не следует создавать.

defaultdict не может этого сделать. Вместо этого необходимо использовать обычный dict с методами get и setdefault.

теоретически setdefault все равно будет удобно, если вы иногда хотите установить значение по умолчанию, а иногда и нет. В реальной жизни я не сталкивался с таким прецедентом.

однако, интересный случай использования приходит из стандартной библиотеки (в Python 2.6, _threadinglocal.py):

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

Я бы сказал, что с помощью __dict__.setdefault - это очень полезное дело.

Edit: как это бывает, это единственный пример в стандартная библиотека и это в комментарии. Так что может быть это не достаточно случая, чтобы оправдать существование setdefault. Тем не менее, вот объяснение:

объекты хранят свои атрибуты в тег . Как это бывает,__dict__ атрибут может быть записан в любое время после создания объекта. Это также словарь не defaultdict. Для объектов в общем случае нецелесообразно иметь __dict__ как defaultdict потому что это сделало бы каждый объект, имеющий все законные идентификаторы как атрибуты. Поэтому я не могу предвидеть никаких изменений в объектах Python, избавляющихся от __dict__.setdefault, кроме удаления его вообще, если это было сочтено не полезным.

вот несколько примеров 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'])

один недостаток defaultdict over dict (dict.setdefault), что

Я часто использую 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')

но это менее необходимо, чем это было до defaultdicts существовали.

еще один случай использования, который я не думаю, что был упомянут выше. Иногда вы сохраняете Cache dict объектов по их идентификатору, где первичный экземпляр находится в кэше, и вы хотите установить кэш, когда он отсутствует.

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

это полезно, когда вы всегда хотите сохранить один экземпляр на отдельный идентификатор независимо от того, как вы получаете obj каждый раз. Например, когда атрибуты объекта обновляются в памяти и сохранение в хранилище откладывается.

один очень важный прецедент я просто наткнулся:dict.setdefault() отлично подходит для многопоточного кода, когда вы хотите только один канонический объект (в отличие от нескольких объектов, которые оказываются равными).

например,(Int)Flag перечисление в Python 3.6.0 имеет ошибку: если несколько потоков конкурируют за композитный (Int)Flag участник, там может оказаться более одного:

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() как последний шаг сохранения вычисляемый составной элемент -- если другой уже был сохранен, то он используется вместо нового, гарантируя уникальные члены перечисления.

как большинство ответов государственной setdefault или defaultdict позволит вам установить значение по умолчанию, если ключ не существует. Однако я хотел бы указать на небольшое предостережение в отношении случаев использования setdefault. Когда интерпретатор Python выполняет setdefaultОн всегда будет оценивать второй аргумент функции, даже если ключ существует в словаре. Например:

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 также был выполнен, хотя 2 уже существовали в словаре. Это становится особенно важно, если вы планируете использовать setdefault например для оптимизации, как memoization. Если вы добавляете рекурсивный вызов функции в качестве второго аргумента в setdefault, вы не получите никакой производительности из него, как Python всегда будет вызывать функцию рекурсивно.

[Edit]очень неправильно! setdefault всегда будет вызывать long_computation, Python стремится.

расширение ответа Таттла. Для меня лучшим вариантом использования является механизм кэша. Вместо:

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

который потребляет 3 строки и 2 или 3 поиска,Я бы с удовольствием написал:

return memo.setdefault(x, long_computation(x))

Мне нравится ответ дан здесь:

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

короче говоря, решение (в приложениях, не критичных к производительности) должно приниматься на основе того, как вы хотите обрабатывать поиск пустых ключей вниз по течению (то есть.KeyError по сравнению со значением по умолчанию).

другой вариант использования для setdefault() и когда вы не хотите перезаписать значение уже установленного ключа. defaultdict перезаписывает, в то время как setdefault() нет. Для вложенных словарей чаще всего требуется установить значение по умолчанию только в том случае, если ключ еще не установлен, потому что вы не хотите удалять настоящий вспомогательный словарь. Это когда вы используете setdefault().

пример defaultdict:

>>> 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}

я переписал принятый ответ и облегчил его для новичков.

#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'],}

Comments

    Ничего не найдено.