update for create python dictionary override autovivification

create - python for dictionary key value



Cómo cambiar el comportamiento de dict() para una instancia (6)

¿Quieres simplemente imprimirlo como un dict? utilizar esta:

from collections import defaultdict class RecursiveDict(defaultdict): '''''' A recursive default dict. >>> a = RecursiveDict() >>> a[1][2][3] = 4 >>> a.dictify() {1: {2: {3: 4}}} >>> dict(a) {1: {2: {3: 4}}} '''''' def __init__(self): super(RecursiveDict, self).__init__(RecursiveDict) def dictify(self): ''''''Get a standard dictionary of the items in the tree.'''''' return dict([(k, (v.dictify() if isinstance(v, dict) else v)) for (k, v) in self.items()]) def __dict__(self): ''''''Get a standard dictionary of the items in the tree.'''''' print [(k, v) for (k, v) in self.items()] return dict([(k, (dict(v) if isinstance(v, dict) else v)) for (k, v) in self.items()]) def __repr__(self): return repr(self.dictify())

Tal vez usted está buscando __missing__ :

class RecursiveDict(dict): '''''' A recursive default dict. >>> a = RecursiveDict() >>> a[1][2][3] = 4 >>> a {1: {2: {3: 4}}} >>> dict(a) {1: {2: {3: 4}}} '''''' def __missing__(self, key): self[key] = self.__class__() return self[key]

Así que estoy escribiendo una clase que extiende un diccionario que en este momento usa un método "dictificar" para transformarse en un dictado. Sin embargo, lo que me gustaría hacer es cambiarlo para que llamar a dict () en el objeto tenga el mismo comportamiento, pero no sé qué método debo reemplazar. ¿No es esto posible, o me estoy perdiendo algo totalmente obvio? (Y sí, sé que el código siguiente no funciona, pero espero que ilustre lo que estoy tratando de hacer).

from collections import defaultdict class RecursiveDict(defaultdict): '''''' A recursive default dict. >>> a = RecursiveDict() >>> a[1][2][3] = 4 >>> a.dictify() {1: {2: {3: 4}}} '''''' def __init__(self): super(RecursiveDict, self).__init__(RecursiveDict) def dictify(self): ''''''Get a standard dictionary of the items in the tree.'''''' return dict([(k, (v.dictify() if isinstance(v, dict) else v)) for (k, v) in self.items()]) def __dict__(self): ''''''Get a standard dictionary of the items in the tree.'''''' print [(k, v) for (k, v) in self.items()] return dict([(k, (dict(v) if isinstance(v, dict) else v)) for (k, v) in self.items()])

EDITAR: Para mostrar el problema más claramente:

>>> b = RecursiveDict() >>> b[1][2][3] = 4 >>> b defaultdict(<class ''__main__.RecursiveDict''>, {1: defaultdict(<class ''__main__.RecursiveDict''>, {2: defaultdict(<class ''__main__.RecursiveDict''>, {3: 4})})}) >>> dict(b) {1: defaultdict(<class ''__main__.RecursiveDict''>, {2: defaultdict(<class ''__main__.RecursiveDict''>, {3: 4})})} >>> b.dictify() {1: {2: {3: 4}}}

Quiero que dict (b) sea igual que b.dictify ()


Necesita anular __iter__ .

def __iter__(self): return iter((k, (v.dictify() if isinstance(v, dict) else v)) for (k, v) in self.items())

En lugar de self.items() , debes usar self.iteritems() en Python 2.

Edit: OK, este parece ser tu problema:

>>> class B(dict): __iter__ = lambda self: iter(((1, 2), (3, 4))) ... >>> b = B() >>> dict(b) {} >>> class B(list): __iter__ = lambda self: iter(((1, 2), (3, 4))) ... >>> b = B() >>> dict(b) {1: 2, 3: 4}

Por lo tanto, este método no funciona si el objeto al que está llamando dict() es una subclase de dict.

Edit 2: Para ser claros, defaultdict es una subclase de dict . dict (a_defaultdict) sigue siendo un no-op.


No hay nada malo con su enfoque, pero esto es similar a la función de Autovivificación de Perl, que se ha implementado en Python en esta pregunta . Atrezzo a @nosklo por esto.

class RecursiveDict(dict): """Implementation of perl''s autovivification feature.""" def __getitem__(self, item): try: return dict.__getitem__(self, item) except KeyError: value = self[item] = type(self)() return value >>> a = RecursiveDict() >>> a[1][2][3] = 4 >>> dict(a) {1: {2: {3: 4}}}

EDITAR

Según lo sugerido por @Rosh Oxymoron, el uso de __missing__ resulta en una implementación más concisa. Requiere Python> = 2.5

class RecursiveDict(dict): """Implementation of perl''s autovivification feature.""" def __missing__(self, key): value = self[key] = type(self)() return value


No puedes hacerlo

Borré mi respuesta anterior, porque después de ver el código fuente descubrí que si llama a dict(d) en una d que es una subclase de dict , hace una copia rápida del hash subyacente en C y devuelve una nueva objeto de dictado.

Lo siento.

Si realmente desea este comportamiento, deberá crear una clase RecursiveDict que no herede de dict , e implementar la interfaz __iter__ .


Una vez que tenga su función de dictado trabajando simplemente haga

dict = dictify

Actualización: Aquí hay una forma corta de tener este dictado recursivo:

>>> def RecursiveDict(): ... return defaultdict(RecursiveDict)

Entonces tú puedes:

d[1][2][3] = 5 d[1][2][4] = 6 >>> d defaultdict(<function ReturnsRecursiveDict at 0x7f3ba453a5f0>, {1: defaultdict(<function ReturnsRecursiveDict at 0x7f3ba453a5f0>, {2: defaultdict(<function ReturnsRecursiveDict at 0x7f3ba453a5f0>, {3: 5, 4: 6})})})

No veo una buena manera de implementar dictificar.


edit : como ironchefpython señaló en los comentarios, esto no está realmente haciendo lo que pensé que hizo, ya que en mi ejemplo b[1] todavía es un RecursiveDict . Esto todavía puede ser útil, ya que esencialmente obtienes un objeto muy similar a la respuesta de Rob Cowie, pero está basado en el valor defaultdict .

Puedes obtener el comportamiento que deseas (o algo muy similar) al anular __repr__ , __repr__ esto:

class RecursiveDict(defaultdict): def __init__(self): super(RecursiveDict, self).__init__(RecursiveDict) def __repr__(self): return repr(dict(self)) >>> a = RecursiveDict() >>> a[1][2][3] = 4 >>> a # a looks like a normal dict since repr is overridden {1: {2: {3: 4}}} >>> type(a) <class ''__main__.RecursiveDict''> >>> b = dict(a) >>> b # dict(a) gives us a normal dictionary {1: {2: {3: 4}}} >>> b[5][6] = 7 # obviously this won''t work anymore Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 5 >>> type(b) <type ''dict''>

Puede haber una mejor manera de llegar a una vista de diccionario normal del punto por defaultdict que dict(self) pero no pude encontrar una, comente si sabe cómo hacerlo.