Почему python dict.update() не возвращает объект?

Я пытаюсь сделать:

award_dict = {
    "url" : "http://facebook.com",
    "imageurl" : "http://farm4.static.flickr.com/3431/3939267074_feb9eb19b1_o.png",
    "count" : 1,
}

def award(name, count, points, desc_string, my_size, parent) :
    if my_size > count :
        a = {
            "name" : name,
            "description" : desc_string % count,
            "points" : points,
            "parent_award" : parent,
        }
        a.update(award_dict)
        return self.add_award(a, siteAlias, alias).award

Но если в функции было действительно громоздко, и я бы скорее сделал:

        return self.add_award({
            "name" : name,
            "description" : desc_string % count,
            "points" : points,
            "parent_award" : parent,
        }.update(award_dict), siteAlias, alias).award

Почему обновление не возвращает объект, чтобы вы могли цепью?

JQuery делает это для цепочки. Почему это не приемлемо для python?

Ответ 1

Python в основном реализует прагматически окрашенный аромат разделение команд-запросов: mutators возвращают None (с прагматически индуцированными исключениями, такими как pop; -), поэтому их нельзя путать с аксессуарами (и в том же ключе назначение не является выражением, существует разделение выражения выражения и т.д.).

Это не означает, что не существует много способов слияния вещей, когда вы действительно этого хотите, например, dict(a, **award_dict) делает новый dict так же, как тот, который вам кажется желательным .update возвращен - так почему не используйте ЭТО, если вы действительно считаете это важным?

Изменить: btw, не нужно, в вашем конкретном случае, создать a на этом пути:

dict(name=name, description=desc % count, points=points, parent_award=parent,
     **award_dict)

создает единый dict с точно такой же семантикой, что и ваш a.update(award_dict) (включая, в случае конфликтов, тот факт, что записи в award_dict переопределяют те, которые вы даете явно, для получения другой семантики, т.е. для имеют явные записи, "выигрывающие" такие конфликты, передают award_dict как единственный позиционный arg, перед ключевым словом, и лишены формы ** - dict(award_dict, name=name и т.д. и т.д.).

Ответ 2

API Python, по договоренности, различает процедуры и функции. Функции вычисляют новые значения из своих параметров (включая любой целевой объект); процедуры изменяют объекты и ничего не возвращают (т.е. они возвращают None). Таким образом, процедуры имеют побочные эффекты, функции - нет. update - это процедура, поэтому она не возвращает значение.

Мотивация для этого заключается в том, что в противном случае вы можете получить нежелательные побочные эффекты. Рассмотрим

bar = foo.reverse()

Если реверс (который меняет список на месте) также вернет список, пользователи могут подумать, что обратный возвращает новый список, который присваивается бару, и никогда не замечает, что foo также изменяется. Сделав обратный возврат None, они сразу же узнают, что бар не является результатом разворота, и будет выглядеть более близким, каков эффект обратного.

Ответ 3

>>> dict_merge = lambda a,b: a.update(b) or a
>>> dict_merge({'a':1, 'b':3},{'c':5})
{'a': 1, 'c': 5, 'b': 3}

Обратите внимание, что, возвращая объединенный dict, он изменяет первый параметр на месте. Так dict_merge (a, b) изменит a.

Или, конечно же, вы можете сделать все это inline:

>>> (lambda a,b: a.update(b) or a)({'a':1, 'b':3},{'c':5})
{'a': 1, 'c': 5, 'b': 3}

Ответ 4

не хватает репутации для комментария слева на верхний

@beardc это не похоже на вещь CPython. PyPy дает мне "TypeError: ключевые слова должны быть строками"

Решение с **kwargs работает только потому, что словарь, который должен быть объединен, имеет ключи строки типа.

то есть.

>>> dict({1:2}, **{3:4})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: keyword arguments must be strings

против

>>> dict({1:2}, **{'3':4})
{1: 2, '3': 4}

Ответ 5

Не то, чтобы это было неприемлемо, а скорее, что dicts не были реализованы таким образом.

Если вы посмотрите на Django ORM, он широко использует цепочку. Его не обескураживают, вы даже можете унаследовать от dict и только переопределять update для обновления и return self, если вы действительно этого хотите.

class myDict(dict):
    def update(self, *args):
        dict.update(self, *args)
        return self

Ответ 6

Это легко как:

(lambda d: d.update(dict2) or d)(d1)

Ответ 7

как можно ближе к предлагаемому решению, поскольку я мог бы получить

from collections import ChainMap

return self.add_award(ChainMap(award_dict, {
    "name" : name,
    "description" : desc_string % count,
    "points" : points,
    "parent_award" : parent,
}), siteAlias, alias).award

Ответ 8

import itertools
dict_merge = lambda *args: dict(itertools.chain(*[d.iteritems() for d in args]))

Ответ 9

Просто попробовал это сам в Python 3.4 (так что не смог использовать синтаксис fancy {**dict_1, **dict_2}).

Я хотел иметь возможность иметь нестрочные ключи в словарях, а также предоставлять произвольное количество словарей.

Кроме того, я хотел создать новый словарь, поэтому я решил не использовать collections.ChainMap (вроде того, что я не хотел использовать dict.update изначально.

Вот что я написал:

def merge_dicts(*dicts):
    all_keys  = set(k for d in dicts for k in d.keys())
    chain_map = ChainMap(*reversed(dicts))
    return {k: chain_map[k] for k in all_keys}

merge_maps({'1': 1}, {'2': 2, '3': 3}, {'1': 4, '3': 5})
# {'1': 4, '3': 5, '2': 2}

Ответ 10

Для тех, кто опаздывает на вечеринку, я собрал некоторое время (Py 3.7), показывая, что .update() основе .update() выглядят немного (~ 5%) быстрее, когда сохраняются входные данные, и заметно (~ 30%) быстрее, когда просто обновление на месте.

Как обычно, все критерии должны быть взяты с крошкой соли.

def join2(dict1, dict2, inplace=False):
    result = dict1 if inplace else dict1.copy()
    result.update(dict2)
    return result


def join(*items):
    iter_items = iter(items)
    result = next(iter_items).copy()
    for item in iter_items:
        result.update(item)
    return result


def update_or(dict1, dict2):
    return dict1.update(dict2) or dict1


d1 = {i: str(i) for i in range(1000000)}
d2 = {str(i): i for i in range(1000000)}

%timeit join2(d1, d2)
# 258 ms ± 1.47 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit join(d1, d2)
# 262 ms ± 2.97 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit dict(d1, **d2)
# 267 ms ± 2.74 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit {**d1, **d2}
# 267 ms ± 1.84 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Временные интервалы для операций на месте немного сложнее, поэтому их необходимо будет модифицировать вместе с дополнительной операцией копирования (первый тайминг только для справки):

%timeit dd = d1.copy()
# 44.9 ms ± 495 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit dd = d1.copy(); join2(dd, d2)
# 296 ms ± 2.05 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit dd = d1.copy(); join2(dd, d2, True)
# 234 ms ± 1.02 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit dd = d1.copy(); update_or(dd, d2)
# 235 ms ± 1.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Ответ 11

Для тех, кто опаздывает на вечеринку, я собрал время (Py 3.7):

def join2(dict1, dict2, inplace=False):
    result = dict1 if inplace else dict1.copy()
    result.update(dict2)
    return result


def join(*items):
    iter_items = iter(items)
    result = next(iter_items).copy()
    for item in iter_items:
        result.update(item)
    return result


def update_or(dict1, dict2):
    return dict1.update(dict2) or dict1


d1 = {i: str(i) for i in range(1000000)}
d2 = {str(i): i for i in range(1000000)}

%timeit join2(d1, d2)                                                                                                                             
# 306 ms ± 2.34 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit join(d1, d2)                                                                                                                                    
# 316 ms ± 14.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit dict(d1, **d2)                                                                                                                                  
# 423 ms ± 2.55 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit {**d1, **d2}                                                                                                                                    
# 427 ms ± 3.08 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

join2(inplace=True) и update_or() несколько разные звери