2009-09-21 6 views
87

я пытаюсь сделать:Почему 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?

ответ

154

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

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

Edit: Кстати, нет необходимости в вашем конкретном случае, чтобы создать a по пути, либо:

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

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

+0

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

+0

@Paul, и это именно то, что вы делаете - с двумя утверждениями (гораздо более удобочитаемыми, чем вложенные вами пути), которые вам «по-настоящему громоздки». Редактирование моего ответа, чтобы показать, как избежать создания 'a' в целом, btw, –

+12

' dict (a, ** award_dict) 'просто скалы и было именно то, что я хотел - спасибо за это! (использовал 'dict (d1.items() + d2.items()) 'before) –

3

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

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

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

Спасибо, это может патч dict, я просто хотел знать, почему dict() не разрешил эту функциональность сам (так как это так же просто, как вы демонстрируете). Использует ли Django patch так? –

28

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

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

bar = foo.reverse() 

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

+1

Спасибо. t reverse также дают возможность не делать это на месте? Производительность? Выполнение 'reverse (foo)' кажется странным. –

+0

Addin g вариант был бы неуместным: он изменил бы характер метода в зависимости от параметра. Однако методы должны иметь фиксированные типы возврата (например, есть случаи, когда это правило нарушено). Легко создать обратную копию: просто сделайте копию (используя 'bar = foo [:]'), а затем верните копию. –

+2

Я думаю, что причина объяснительна. В 'bar = foo.reverse()', вы можете подумать, что 'foo' не изменяется. Чтобы избежать путаницы, у вас есть как 'foo.reverse()', так и 'bar = reverse (foo)'. –

9
>>> 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.

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

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

-1 'lambda' не следует использовать так, вместо этого используйте обычную функцию' def' вместо – jamylak

+2

Даже не нужно лямбда, просто используйте 'a.update (b) или' – Pycz

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

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

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

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

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 
0

Просто пытались это сам в Python 3.4 (так не было способный использовать фантастический синтаксис {**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}