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]))