Dado que no sabemos de antemano si necesitamos un diccionario, o una lista, no se puede combinar la autovibración con las listas. A menos que, trabajando sobre la respuesta de Nosklo a partir de la pregunta vinculada, añada "características" de lista al diccionario subyacente. Básicamente asumiendo un orden de "clasificación" para las claves, y siempre usándolo con los métodos de la lista. He hecho esto como un ejemplo:
class AutoVivification(dict):
"""Implementation of perl's autovivification feature. Has features from both dicts and lists,
dynamically generates new subitems as needed, and allows for working (somewhat) as a basic type.
"""
def __getitem__(self, item):
if isinstance(item, slice):
d = AutoVivification()
items = sorted(self.iteritems(), reverse=True)
k,v = items.pop(0)
while 1:
if (item.start < k < item.stop):
d[k] = v
elif k > item.stop:
break
if item.step:
for x in range(item.step):
k,v = items.pop(0)
else:
k,v = items.pop(0)
return d
try:
return dict.__getitem__(self, item)
except KeyError:
value = self[item] = type(self)()
return value
def __add__(self, other):
"""If attempting addition, use our length as the 'value'."""
return len(self) + other
def __radd__(self, other):
"""If the other type does not support addition with us, this addition method will be tried."""
return len(self) + other
def append(self, item):
"""Add the item to the dict, giving it a higher integer key than any currently in use."""
largestKey = sorted(self.keys())[-1]
if isinstance(largestKey, str):
self.__setitem__(0, item)
elif isinstance(largestKey, int):
self.__setitem__(largestKey+1, item)
def count(self, item):
"""Count the number of keys with the specified item."""
return sum([1 for x in self.items() if x == item])
def __eq__(self, other):
"""od.__eq__(y) <==> od==y. Comparison to another AV is order-sensitive
while comparison to a regular mapping is order-insensitive. """
if isinstance(other, AutoVivification):
return len(self)==len(other) and self.items() == other.items()
return dict.__eq__(self, other)
def __ne__(self, other):
"""od.__ne__(y) <==> od!=y"""
return not self == other
Esto sigue la característica de autovigilancia básica de generarse dinámicamente para las llaves dud. Sin embargo, también implementa algunos de los métodos listed here. Esto le permite actuar como una cosa cuasi-lista/dict.
Para el resto de las funciones de la lista, agregue los métodos enumerados. Lo estoy tratando como un diccionario con los métodos de lista. Si se llama a un método de lista, se realiza una suposición sobre el orden de los elementos que se retienen, es decir, que las cadenas se ordenan menos que los enteros, y que las claves están siempre en orden "ordenado".
También es compatible con la adición, como un ejemplo de these methods. Esto viene de mi propio caso de uso. Necesitaba agregar elementos de un diccionario de AutoVivified, pero si no existe, se crea y se devuelve un nuevo objeto AutoVivification
.No tienen ningún "valor" número entero y por lo que no pueden hacer esto:
rp = AutoVivification()
rp['a']['b'] = 3
rp['a']['b'] + rp['q']
Eso contradice el objetivo, ya que no sé si alguna cosa va a estar ahí, pero yo quiero un defecto de todos modos. Así que le agregué los métodos __add__
y __radd__
. Utilizan el length
del diccionario subyacente como el valor integer
, por lo que un objeto AV recién creado tiene un valor de cero para la suma. Si una clave tiene algo más que un objeto AV, obtendremos el método de adición de esa cosa, si se implementa.
Hermosa solución. Muchas gracias. – Zhang18
Un ejemplo de python que tiene una, y solo una, forma obvia de hacer algo :) – Brian
En realidad, me di cuenta de que esta respuesta solo funciona para 2 niveles de dict y un 3er nivel de lista. ¿Hay alguna manera de ser agnóstico? es decir, después de declarar a, puedo hacer 'a ['x']. append ('z')' o 'a ['x'] ['y'] ['w']. append ('z') ¿? Gracias. – Zhang18