Как скопировать текст и изменить его в одной строке кода - PullRequest
30 голосов
/ 05 апреля 2011

Очень часто мне нужно создавать диктанты, которые отличаются друг от друга на один-два предмета.Вот что я обычно делаю:

setup1 = {'param1': val1, 
            'param2': val2,
            'param3': val3,
            'param4': val4,
            'paramN': valN}

setup2 = copy.deepcopy(dict(setup1))
setup2.update({'param1': val10, 
                   'param2': val20})

Тот факт, что в программе есть точка, в которой setup2 является идентичной копией setup1, заставляет меня нервничать, так как я боюсь, что приВ какой-то момент жизни программы две строки могут быть разделены, что является скользким уклоном в сторону слишком большого количества ошибок.

В идеале я хотел бы иметь возможность выполнить это действие в одной строке кода (что-то вроде этого):

setup2 = dict(setup1).merge({'param1': val10, 
                        'param2': val20})

Конечно, я могу использовать точку с запятой, чтобы сжать две команды водна физическая линия, но это выглядит довольно уродливо для меня.Есть ли другие варианты?

Ответы [ 8 ]

42 голосов
/ 12 октября 2017

Самый простой способ, на мой взгляд, примерно такой:

new_dict = {**old_dict, 'changed_val': value, **other_new_vals_as_dict}
21 голосов
/ 05 апреля 2011
setup2 = dict(setup1.items() + {'param1': val10, 'param2': val20}.items())

Таким образом, если новые ключи не существуют в setup1, они добавляются, в противном случае они заменяют старые пары ключ / значение.

18 голосов
/ 30 января 2016

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

new = dict(old, a=1, b=2, c=3)

# You can also unpack your modifications
new = dict(old, **mods)

Это эквивалентно:

new = old.copy()
new.update({"a": 1, "b": 2, "c": 3})

Источник

Примечание: dict.copy() создает мелкую копию.

15 голосов
/ 05 апреля 2011

Решение

Создайте для этого функцию.

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

def copy_dict(source_dict, diffs):
    """Returns a copy of source_dict, updated with the new key-value
       pairs in diffs."""
    result=dict(source_dict) # Shallow copy, see addendum below
    result.update(diffs)
    return result

И теперь копия является атомарной, при условии, что никакие потоки не вовлечены:

setup2=copy_dict(setup1, {'param1': val10, 'param2': val20})

Приложение - глубокая копия

Для примитивов (целых чисел и строк) глубокое копирование не требуется:

>>> d1={1:'s', 2:'g', 3:'c'}
>>> d2=dict(d1)
>>> d1[1]='a'
>>> d1
{1: 'a', 2: 'g', 3: 'c'}
>>> d2
{1: 's', 2: 'g', 3: 'c'}

Если вам нужна глубокая копия, используйте модуль copy:

result=copy.deepcopy(source_dict) # Deep copy

вместо:

result=dict(setup1)               # Shallow copy

Убедитесь, что все объекты в вашем словаре поддерживают глубокое копирование (любой объект, который может быть pickled, должен это делать).

13 голосов
/ 05 апреля 2011
setup2 = dict((k, {'param1': val10, 'param2': val20}.get(k, v))
              for k, v in setup1.iteritems())

Это работает, только если все ключи словаря обновлений уже содержатся в setup1.

Если все ваши ключи строковые, вы также можете сделать

setup2 = dict(setup1, param1=val10, param2=val20)
2 голосов
/ 13 сентября 2012

Если вам просто нужно создать новый dict с элементами из более чем одного dict, вы можете использовать:

dict(a.items() + b.items())

Если и у «a», и у «b» есть один и тот же ключ, результат будетиметь значение от б.Если вы используете Python 3, конкатенация не будет работать, но вы можете сделать то же самое, заморозив генераторы в списках или используя функцию itertools.chain.

0 голосов
/ 25 августа 2017

Это расширение к хорошему ответу Адама Матана:

def copy_dict(d, diffs={}, **kwargs):
    res = dict(d)
    res.update(diffs)
    res.update(kwargs)
    return res

Единственное отличие - это добавление kwargs.Теперь можно написать

setup2 = copy_dict(setup1, {'param1': val10, 'param2': val20})

или

setup2 = copy_dict(setup1, param1=val10, param2=val20)
0 голосов
/ 05 апреля 2011

Вы можете написать свой собственный класс, используя UserDict обертку, и просто добавить dict s как

# setup1 is of Dict type (see below)
setup2 = setup1 + {'param1': val10}

Все, что вам нужно сделать, это

  • Определить новыйкласс, использующий UserDict в качестве базового класса
  • Реализация для него __add__ метода.

Что-то вроде:

class Dict(dict):
    def __add__(self, _dict):
        if isinstance(_dict, dict):
            tmpdict = Dict(self)
            tmpdict.update(_dict)
            return tmpdict

        else:
            raise TypeError

    def __radd__(self, _dict):
         return Dict.__add__(self, _dict)
...