2009-09-21 15 views
87

usiłuję zrobić:Dlaczego program python dict.update() nie zwraca obiektu?

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 

Ale jeśli się naprawdę uciążliwe w funkcji, a ja bym raczej zrobić:

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

Dlaczego nie aktualizuje zwrócić przedmiot, dzięki czemu można łańcuch?

JQuery robi to, aby zrobić łańcuch. Dlaczego nie jest to dopuszczalne w pythonie?

Odpowiedz

154

Pythona większości realizacji pragmatyczne zabarwiony smak command-query separation: mutatory powrotu None (z pragmatyczne indukowanych wyjątkami jak pop ;-) więc nie może być mylony z dostępowych (w tym samym duchu, zadanie nie jest wyrazem , oddzielenie wyrażenia oświadczenie jest tam, i tak dalej).

To nie znaczy, że nie ma wielu sposobów na scalanie rzeczy, kiedy naprawdę chcesz, np. dict(a, **award_dict) tworzy nowy dyktor bardzo podobny do tego, na który wyglądasz, że chcesz, aby .update został zwrócony - dlaczego więc nie użyć TEGO jeśli naprawdę uważasz, że to ważne?

Edit: btw, nie ma potrzeby, w konkretnym przypadku, aby utworzyć a po drodze, albo:

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

tworzy pojedynczy dict z dokładnie tych samych semantyki jak wasz a.update(award_dict) (w tym, w przypadku konfliktów, fakt, że wpisy w award_dict zastępują te, które podajesz jawnie, aby uzyskać inną semantykę, tj. aby jawne wpisy "wygrywały" takie konflikty, przechodzą award_dict jako jedyne pozycyjnie arg, przed ke yword i pozbawiony formularza ** - dict(award_dict, name=name itp.).

+0

Cóż, to stworzy kolejny słownik po tym, jak będę musiał zrobić. Chciałem stworzyć dykt, a następnie dodać kilka innych wartości, a następnie nadać mu funkcję. –

+0

@Paul, i to jest dokładnie to, co robisz - z dwoma oświadczeniami (o wiele bardziej czytelnymi niż w sposób, w jaki chciałeś), które dla Ciebie "wydawały się naprawdę uciążliwe". Edytuję moją odpowiedź, aby pokazać, jak uniknąć tworzenia 'a' w ogóle, btw, –

+12

' dict (a, ** award_dict) 'po prostu kołysze i było dokładnie tym, czego chciałem - dziękuję za to! (używał 'dict (d1.items() + d2.items()) 'before) –

3

To nie jest to, że nie jest dopuszczalne, ale raczej, że dicts nie zostały zaimplementowane w ten sposób.

Jeśli spojrzeć na ORM Django, to w dużym stopniu wykorzystuje łańcuch. Nie zniechęcaj się, możesz nawet odziedziczyć po dict i tylko zastąpić update, aby zaktualizować i return self, jeśli naprawdę chcesz.

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

Dziękuję, to może łatać dict, chciałem tylko wiedzieć, dlaczego dict() nie zezwala na tę funkcjonalność (ponieważ jest to tak łatwe, jak to pokazujesz) Czy łata Django dyktuje w ten sposób? –

28

API Pythona, według konwencji, rozróżnia procedury i funkcje. Funkcje obliczają nowe wartości z ich parametrów (w tym dowolnego obiektu docelowego); procedury modyfikują obiekty i nie zwracają niczego (tzn. zwracają Brak). Tak więc procedury mają skutki uboczne, a funkcje nie. update jest procedurą, więc nie zwraca wartości.

Motywacją do zrobienia tego w ten sposób jest to, że w przeciwnym razie mogą wystąpić niepożądane skutki uboczne. Rozważmy

bar = foo.reverse() 

Jeśli odwróconej (który odwraca listy na miejscu) będzie również zwracać listę, użytkownicy mogą myśleć, że reverse zwraca nową listę, która zostanie przypisana do baru, i nigdy nie zauważy, że również zostanie zmodyfikowany foo. Dokonując wstecznego powrotu Brak, natychmiast rozpoznają, że słupek nie jest wynikiem odwrócenia, i będzie bliżej przyjrzeć się efektowi odwrotności.

+1

Dziękuję. t rewers także dają opcję, aby nie zrobić tego w miejscu? Wydajność? robienie 'reverse (foo)' wydaje się dziwne. –

+0

Dodawanie opcja byłaby niewłaściwa: zmieniłaby charakter metody w zależności od parametru. Jednak metody powinny mieć naprawione typy zwrotu (niestety są przypadki, w których ta reguła jest zepsuta). Łatwo jest utworzyć kopię przywróconą: po prostu utwórz kopię (używając 'bar = foo [:]'), a następnie przywróć kopię. –

+2

Myślę, że powodem jest wyraźność. W 'bar = foo.reverse()' możesz pomyśleć, że 'foo' nie jest modyfikowane. Aby uniknąć nieporozumień, masz zarówno 'foo.reverse()' jak i '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} 

Należy zwrócić uwagę, że poza zwrotem scalonego tekstu modyfikuje on pierwszy parametr na miejscu. Zatem dict_merge (a, b) zmodyfikuje a.

Albo, oczywiście, można zrobić to wszystko inline:

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

-1 'lambda' nie powinno być używane w ten sposób, zamiast tego użyj konwencjonalnej funkcji' def' zamiast – jamylak

+2

Nawet nie potrzebujesz lambda, po prostu użyj 'a.update (b) lub a' – Pycz

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

mało reputacji za komentarz w lewo na górze odpowiedź

@beardc to nie wydaje się być Rzecz CPythona. Pypy daje mi „TypeError: słowa kluczowe muszą być ciągi”

Rozwiązanie z **kwargs działa tylko dlatego, że słownik zostać połączone tylko ma klucze typu string.

tj

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

vs

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

tak blisko do proponowanego rozwiązania, jak mogę dostać

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

właśnie próbuje to sobie w Pythonie 3.4 (tak nie było w stanie użyć fantazyjnej składni {**dict_1, **dict_2}).

Chciałem móc mieć klucze nie będące ciągami w słownikach, a także zapewnić dowolną liczbę słowników.

Również chciałem zrobić nowy słownik, więc zdecydowaliśmy się nie używać collections.ChainMap (kinda powód nie chciałem używać dict.update początkowo

Oto, co skończyło się na piśmie:.

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} 
Powiązane problemy