dict python language-design language-features

python - ¿Por qué un pitton dict.update() no devuelve el objeto?



dict() python (8)

He estado probando esto en Python 3.4 (por lo que no pude usar la elegante sintaxis {**dict_1, **dict_2} ).

Quería poder tener claves que no sean cadenas en los diccionarios, así como proporcionar una cantidad arbitraria de diccionarios.

Además, quería crear un nuevo diccionario, así que opté por no usar collections.ChainMap (un poco la razón por la que no quería usar dict.update inicialmente).

Esto es lo que terminé escribiendo:

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}

Estoy tratando de hacer:

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

Pero si se sintiera realmente engorroso en la función, y hubiera preferido hacerlo:

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

¿Por qué la actualización no devuelve el objeto para que pueda encadenar?

JQuery hace esto para encadenar. ¿Por qué no es aceptable en Python?


La API de Python, por convención, distingue entre procedimientos y funciones. Las funciones calculan nuevos valores a partir de sus parámetros (incluido cualquier objeto de destino); los procedimientos modifican objetos y no devuelven nada (es decir, devuelven None). Entonces los procedimientos tienen efectos secundarios, las funciones no. la actualización es un procedimiento, por lo tanto, no devuelve un valor.

La motivación para hacerlo de esa manera es que, de lo contrario, puede obtener efectos secundarios indeseables. Considerar

bar = foo.reverse()

Si el reverso (que invierte la lista in situ) también devuelve la lista, los usuarios pueden pensar que el reverso devuelve una nueva lista que se asigna a la barra, y nunca se dan cuenta de que foo también se modifica. Al hacer que Invertir devuelva Ninguno, inmediatamente reconocen que la barra no es el resultado de la inversión, y se verán más cerca de cuál es el efecto de invertir.


No es que no sea aceptable, sino que los dicts no se implementaron de esa manera.

Si observas el ORM de Django, hace un uso extensivo del encadenamiento. No está desaconsejado, incluso puede heredar de dict y solo anular la update para actualizar y return self , si realmente lo desea.

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


Python está principalmente implementando un sabor pragmático de separación de consulta de comandos : los mutadores devuelven None (con excepciones inducidas pragmáticamente como pop ;-) por lo que no pueden confundirse con los accessors (y en la misma línea, la asignación no es una expresión, la separación expresión-expresión está allí, y así sucesivamente).

Eso no significa que no hay muchas formas de fusionar las cosas cuando realmente lo desea, por ejemplo, dict(a, **award_dict) hace una nueva dicción muy parecida a la que parece desear. .update devuelta - por lo tanto ¿Por qué no usar ESO si realmente sientes que es importante?

Editar : por cierto, no es necesario, en su caso específico, para crear a en el camino, ya sea:

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

crea un solo dict con exactamente la misma semántica que tu a.update(award_dict) (incluyendo, en caso de conflictos, el hecho de que las entradas en award_dict anulan las que estás dando explícitamente, para obtener las otras semánticas, es decir, para tener explícita las entradas que "ganan" tales conflictos, pasan award_dict como el único award_dict posicional , antes de los keyword, y carecen del ** form - dict(award_dict, name=name etc.).


tan cerca de la solución propuesta como pude obtener

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


no hay suficiente reputación para los comentarios que quedan en la respuesta superior

@beardc esto no parece ser cosa de CPython. PyPy me da "TypeError: las palabras clave deben ser cadenas"

La solución con **kwargs solo funciona porque el diccionario que se fusionará solo tiene claves de tipo cadena .

es decir

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


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

Tenga en cuenta que, además de devolver el dict fusionado, modifica el primer parámetro in situ. Entonces dict_merge (a, b) modificará a.

O, por supuesto, puedes hacerlo todo en línea:

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


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