Czy mogę wykonać zamówiony, domyślny dict w Pythonie?
Chciałbym połączyć OrderedDict()
i defaultdict()
z collections
w jeden obiekt, który będzie uporządkowanym, domyślnym dict. Czy to możliwe?
8 answers
Następujący (przy użyciu zmodyfikowanej wersji ten przepis ) działa dla mnie:
from collections import OrderedDict, Callable
class DefaultOrderedDict(OrderedDict):
# Source: http://stackoverflow.com/a/6190500/562769
def __init__(self, default_factory=None, *a, **kw):
if (default_factory is not None and
not isinstance(default_factory, Callable)):
raise TypeError('first argument must be callable')
OrderedDict.__init__(self, *a, **kw)
self.default_factory = default_factory
def __getitem__(self, key):
try:
return OrderedDict.__getitem__(self, key)
except KeyError:
return self.__missing__(key)
def __missing__(self, key):
if self.default_factory is None:
raise KeyError(key)
self[key] = value = self.default_factory()
return value
def __reduce__(self):
if self.default_factory is None:
args = tuple()
else:
args = self.default_factory,
return type(self), args, None, None, self.items()
def copy(self):
return self.__copy__()
def __copy__(self):
return type(self)(self.default_factory, self)
def __deepcopy__(self, memo):
import copy
return type(self)(self.default_factory,
copy.deepcopy(self.items()))
def __repr__(self):
return 'OrderedDefaultDict(%s, %s)' % (self.default_factory,
OrderedDict.__repr__(self))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-23 20:47:38
Oto kolejna możliwość, zainspirowana super() Raymonda Hettingera, testowana na Pythonie 2.7.X i 3.4.X:
from collections import OrderedDict, defaultdict
class OrderedDefaultDict(OrderedDict, defaultdict):
def __init__(self, default_factory=None, *args, **kwargs):
#in python3 you can omit the args to super
super(OrderedDefaultDict, self).__init__(*args, **kwargs)
self.default_factory = default_factory
Jeśli sprawdzisz MRO klasy (aka, help(OrderedDefaultDict)
), zobaczysz to:
class OrderedDefaultDict(collections.OrderedDict, collections.defaultdict)
| Method resolution order:
| OrderedDefaultDict
| collections.OrderedDict
| collections.defaultdict
| __builtin__.dict
| __builtin__.object
Co oznacza, że gdy instancja OrderedDefaultDict
jest zainicjalizowana, przechodzi do init OrderedDict
, ale ten z kolei wywoła metody defaultdict
przed wywołaniem __builtin__.dict
, co jest dokładnie tym, czego chcemy.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-16 19:20:28
Oto inne rozwiązanie, nad którym warto pomyśleć, jeśli twój przypadek użycia jest prosty jak mój i niekoniecznie chcesz dodać złożoność implementacji klasy DefaultOrderedDict
do kodu.
from collections import OrderedDict
keys = ['a', 'b', 'c']
items = [(key, None) for key in keys]
od = OrderedDict(items)
(None
jest żądaną wartością domyślną.)
Zauważ, że to rozwiązanie nie będzie działać, jeśli jednym z twoich wymagań jest dynamiczne wstawianie nowych kluczy z wartością domyślną. Kompromis prostoty.
Update 3/13/17 - dowiedziałem się o funkcji wygody dla tego zastosowania case. To samo co wyżej, ale można pominąć linię items = ...
i po prostu:
od = OrderedDict.fromkeys(keys)
Wyjście:
OrderedDict([('a', None), ('b', None), ('c', None)])
A jeśli Twoje klucze są pojedynczymi znakami, możesz po prostu przekazać jeden ciąg znaków:
OrderedDict.fromkeys('abc')
To ma taki sam wynik jak dwa przykłady powyżej.
Możesz również przekazać wartość domyślną jako drugi arg do OrderedDict.fromkeys(...)
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-28 16:04:11
Jeśli chcesz prostego rozwiązania, które nie wymaga klasy, możesz po prostu użyć OrderedDict.setdefault(key, default=None)
lub OrderedDict.get(key, default=None)
. Jeśli dostajesz / ustawiasz tylko z kilku miejsc, powiedzmy w pętli, możesz łatwo po prostu setdefault.
totals = collections.OrderedDict()
for i, x in some_generator():
totals[i] = totals.get(i, 0) + x
Jest jeszcze łatwiej dla list z setdefault
:
agglomerate = collections.OrderedDict()
for i, x in some_generator():
agglomerate.setdefault(i, []).append(x)
Ale jeśli używasz go więcej niż kilka razy, prawdopodobnie lepiej jest skonfigurować klasę, jak w innych odpowiedziach.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-27 09:35:26
Prostsza wersja odpowiedzi @zeekay to:
from collections import OrderedDict
class OrderedDefaultListDict(OrderedDict): #name according to default
def __missing__(self, key):
self[key] = value = [] #change to whatever default you want
return value
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-28 20:59:27
Proste i eleganckie rozwiązanie oparte na @NickBread. Ma nieco inny API do ustawienia fabrycznego, ale dobre wartości domyślne są zawsze miłe.
class OrderedDefaultDict(OrderedDict):
factory = list
def __missing__(self, key):
self[key] = value = self.factory()
return value
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-12 16:33:47
Innym prostym podejściem byłoby użycie metody dictionary get
>>> from collections import OrderedDict
>>> d = OrderedDict()
>>> d['key'] = d.get('key', 0) + 1
>>> d['key'] = d.get('key', 0) + 1
>>> d
OrderedDict([('key', 2)])
>>>
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-30 12:23:06
Przetestowałem domyślny dict i odkryłem, że jest on również posortowany! może to był tylko przypadek, ale w każdym razie możesz użyć funkcji sortowanej:
sorted(s.items())
Myślę, że to prostsze
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-13 13:32:12