Este es un comportamiento bastante interesante, no estoy exactamente seguro de cómo funciona, pero analizaré por qué el comportamiento es así.
En primer lugar, tenga en cuenta que multiprocessing.Manager().dict()
no es un dict
, es un objeto DictProxy
:
>>> d = multiprocessing.Manager().dict()
>>> d
<DictProxy object, typeid 'dict' at 0x7fa2bbe8ea50>
El propósito de la clase DictProxy
es para darle una dict
que es seguro para compartir a través de los procesos, lo que significa que debe implementar algún bloqueo sobre las funciones normales de dict
.
Aparentemente, parte de la implementación aquí es no permitirle acceder directamente a los objetos mutables anidados dentro de un DictProxy
, porque si eso estuviera permitido, podría modificar su objeto compartido de manera que omita todo el bloqueo que hace que DictProxy
sea seguro de usar.
Aquí algunas pruebas de que no se puede acceder a los objetos mutables, que es similar a lo que está pasando con setdefault()
:
>>> d['foo'] = []
>>> foo = d['foo']
>>> id(d['foo'])
140336914055536
>>> id(foo)
140336914056184
Con un diccionario normal, se puede esperar d['foo']
foo
y para que apunte a la misma list object, y las modificaciones a uno modificarían el otro. Como ha visto, este no es el caso para la clase DictProxy
debido al requisito de seguridad adicional del proceso impuesto por el módulo de multiprocesamiento.
edición: La siguiente nota de la multiprocessing documentation aclara lo que estaba tratando de decir anteriormente:
Nota: Las modificaciones a los valores mutables o elementos en dict y lista de servidores proxy no será propagado a través del administrador, porque el proxy no tiene forma de saber cuándo se modifican sus valores o elementos. Para modificar un elemento tal, puede volver a asignar el objeto modificado al proxy contenedor:
# create a list proxy and append a mutable object (a dictionary)
lproxy = manager.list()
lproxy.append({})
# now mutate the dictionary
d = lproxy[0]
d['a'] = 1
d['b'] = 2
# at this point, the changes to d are not yet synced, but by
# reassigning the dictionary, the proxy is notified of the change
lproxy[0] = d
Con base en la información anterior, aquí es cómo se puede reescribir su código original para trabajar con una DictProxy
:
# d.setdefault('foo', []).append({'bar': 'baz'})
d['foo'] = d.get('foo', []) + [{'bar': 'baz'}]
Como Edward Loper sugirieron en los comentarios, editado por encima de código para utilizarget()
en lugar desetdefault()
.
+1. Pero creo que sería más claro volver a escribir el código original para usar 'get' en lugar de' setdefault', ya que el comportamiento especial normal de 'setdefault' no se aplica aquí. Es decir .: 'd ['foo'] = d.get ('foo', []) + [{'bar': 'baz'}]' –
Gracias por esta extensa respuesta. Supuse que dado que el proxy dict envolvía la lista que se le había dado, supuse que estaría bien operarlo. Aparentemente no es así. @Edward: eso es un buen consejo. – Bittrance