2010-03-12 2 views
10

Этот вопрос касается реализации полной автовивитации Perl в Python. Я знаю, что подобные вопросы задавали раньше, и до сих пор лучшим ответом является «What is the best way to implement nested dictionaries in Python?». Тем не менее, я ищу, чтобы сделать это:Как сделать расширенную автовыдавку хэша Python?

a['x']['y'].append('z') 

без объявления a['x']['y'] = [] первым, или, вернее, не заявляющего a['x'] = {} либо. (Обратите внимание на Perl можно сделать push @{$a->{x}{y}}, 'z';.)

Я знаю dict и list классы Сорта не смешиваются, так что это трудно, но я заинтересован в том, чтобы, если кто-то оригинальное решение, вероятно, с участием создании унаследованного класса от dict, но определил новый метод append?

Я также знаю, что это может отбросить некоторых пуристов Python, которые попросят меня придерживаться Perl. Но, даже просто для вызова, я бы хотел кое-что увидеть.

ответ

16
a = collections.defaultdict(lambda: collections.defaultdict(list)) 
+0

Красивое решение. Большое спасибо. – Zhang18

+2

Пример python, имеющий один и только один, очевидный способ сделать что-то :) – Brian

+0

На самом деле я понял, что этот ответ работает только для 2 уровней dict и 3-го уровня списка. Есть ли способ стать агностиком? т.е. после объявления a, я могу сделать либо «a ['x»]. append (' z ') 'или' a [' x '] [' y '] [' w ']. append (' z '). '? Спасибо. – Zhang18

2

Возможно, это решит вашу потребность в любом количестве «измерений» в словаре:

a= collections.defaultdict(list) 

единственное изменение в коде существа:

a['x', 'y'].append('z') 

Конечно, это решение являющийся тем, который вы хотите, зависит от двух условий:

  1. , нужно ли вам легко получить доступ ко всем спискам, например. «Х» в «первом измерении»
  2. ли вы застряли в пути Perl волшебен угодным вам больше :)

Если какой-либо из этих двух условий истинно, мое решение не поможет.

+0

Да для 1. и да для 2, к сожалению. Поэтому мне придется жить с тем фактом, что в этом случае нет никакой аналогии в Python для Perl - своего рода разочарование. – Zhang18

+2

Представьте, что вы встречаетесь, не знаете, Николь Кидман. Вы говорите ей, что это печально, что у нее нет кривых, скажем, Моники Беллуччи. Можете ли вы начать понимать недостаток полезности (и благодати!) Такого заявления? Во всяком случае, притчи в стороне, ISTM, что вы сделали свою мысль, и мир может наконец выдохнуть. – tzot

2

Поскольку мы не знаем заранее, нужен ли нам словарь или список, то вы не можете объединить автовивитацию со списками. Если, не откликая ответ Nosklo из связанного вопроса, вы добавляете список «функций» в базовый словарь. В основном, предполагая «сортировку» заказа для ключей и всегда используя его со списком методов. Я сделал это в качестве примера:

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 

Это следует за основной функцией автообвинения, динамически генерирующей себя для ключей dud. Однако он также реализует некоторые из методов listed here. Это позволяет ему действовать как вещь квази-списка/dict.

Для остальных функций списка добавьте перечисленные методы. Я рассматриваю его как словарь со списком методов. Если вызывается метод списка, то он делает предположение о порядке сохраненных элементов, а именно, что строки сортируются ниже целых чисел и что ключи всегда находятся в порядке сортировки.

Он также поддерживает добавление, как пример these methods. Это происходит из моего собственного варианта использования. Мне нужно было добавлять элементы из словаря AutoVivified, но если он не существует, создается и возвращается новый объект AutoVivification.Они не имеют целое «значение», и поэтому вы не можете сделать это:

rp = AutoVivification() 
rp['a']['b'] = 3 
rp['a']['b'] + rp['q'] 

Это поражения цели, так как я не знаю, если какая-то вещь собирается быть там, но я хочу, по умолчанию в любом случае. Поэтому я добавил методы __add__ и __radd__. Они используют length базового словаря как значение integer, поэтому вновь созданный объект AV имеет значение нуля для добавления. Если у ключа есть что-то помимо AV-объекта, тогда мы получим метод добавления этой вещи, если он будет реализован.

1

Просто расширяя ответ Игнасио, чтобы представить некоторые дополнительные параметры, которые позволяют вам явно запрашивать более волшебное поведение из словарей Python. Ремонтопригодность кода, написанного таким образом, остается сомнительной, но я хотел бы четко сформулировать, что речь идет о «Является ли такая структура данных устойчивой?» (Я сомневаюсь) не «Может ли Python вести себя так?» (это, безусловно, может).

Для поддержки произвольных уровней вложенности для аспекта пространства имен, все, что вам нужно сделать, это имя функции (вместо использования лямбды) и сделать его самоссылающийся:

>>> from collections import defaultdict 
>>> def autodict(): return defaultdict(autodict) 
... 
>>> a = autodict() 
>>> a[1][2][3] = [] 
>>> type(a[1]) 
<class 'collections.defaultdict'> 
>>> type(a[1][2]) 
<class 'collections.defaultdict'> 
>>> type(a[1][2][3]) 
<class 'list'> 
>>> a[1][2][3] 
[] 

Однако, это вводит " проблема ", что вы должны установить список явно, прежде чем можете добавить к нему. Ответ Пайтона к тому, что лежит в setdefault методе, который фактически был вокруг больше, чем collections.defaultdict:

>>> a.setdefault(3, []).append(10) 
>>> a.setdefault(3, []).append(11) 
>>> a[3] 
[10, 11] 
>>> a[2].setdefault(3, []).append(12) 
>>> a[2].setdefault(3, []).append(13) 
>>> a[2][3] 
[12, 13] 
>>> a[1][2].setdefault(3, []).append(14) 
>>> a[1][2].setdefault(3, []).append(15) 
>>> a[1][2][3] 
[14, 15] 

Все collections.defaultdict действительно является сделать общий случай, когда вы всегда передать тот же второй параметр для dict.setdefault гораздо проще в использовании. Для более сложных случаев, таких как этот, вы все равно можете использовать dict.setdefault непосредственно для аспектов, которые не могут обрабатывать collections.defaultdict.

Смежные вопросы