2014-08-28 1 views
1

Я словарем, например, как это:Получить ключевой счет от OrderedDict, где ключом является кортеж

my_dict=collections.OrderedDict([((123, 1), 'qwe'), ((232, 1), 'asd'), ((234, 2), 'zxc'), ((6745, 2), 'aaa'), ((456, 3), 'bbb')]) 

Сочетание кортежа всегда уникален, и я хотел бы сохранить порядок введения, и, следовательно, OrderedDict. У меня есть более чем 10 тысяч предметов в dict. Как я могу эффективно поддерживать счетчик, который дает счет второго элемента в кортеже? В принципе, мне нужно знать счетчик всякий раз, когда я хотел бы добавить/удалить элемент в ключе. Прямо сейчас я просто прохожу через my_dict и получаю счетчик каждый раз, но это кажется очень дорогостоящим.

В приведенном выше примере я хочу выход быть:

1:2 # As in 1 occurs 2 times 
2:2 
3:1 

Прямо сейчас я делаю следующее:

from collections import OrderedDict, Counter 
my_dict = OrderedDict() 
my_dict[(123,1)] = 'qwe' 
my_dict[(232,1)] = 'asd' 
my_dict[(234,2)] = 'zxc' 
my_dict[(6745,2)] = 'aaa' 
my_dict[(456,3)] = 'bbb' 
cnt = [] 
for item in my_dict.keys(): 
    cnt.append(item[1]) 
print Counter(cnt) 

Я не уверен, если это лучший способ, но есть способ переопределить оператора = и pop, чтобы он добавлял или вычитал счет каждый раз, когда я делаю эту операцию?

+0

Вы, вероятно, будете лучше всего, с помощью настраиваемого класса, реализованного '__setitem__' и хранился' 'Counter' и OrderedDict 'как базовые атрибуты. –

+1

Первая строка не имеет эффекта. 'my_dict' присваивается обыкновенной' dict' во второй строке. – jfs

+0

@ J.F.Sebastian Вы правы. Я не думал. Исправлены мои примеры. –

ответ

3

Получение Counter для работы с OrderedDict, вероятно, потребует некоторого подкласса. Вот что-то, что могло бы работать (я реализована только __setitem__ и __getitem__, но если вы хотите более надежной реализации, дайте мне знать):

import collections 

class CountedOrderedDict(collections.OrderedDict): 
    def __init__(self, *args, **kwargs): 
     self.counter = collections.Counter() 
     super(CountedOrderedDict, self).__init__(*args, **kwargs) 

    def __delitem__(self, key): 
     super(CountedOrderedDict, self).__delitem__(key) 
     self.counter[key[1]] -= 1 

    def __setitem__(self, key, value): 
     if key not in self: 
      self.counter[key[1]] += 1 

     super(CountedOrderedDict, self).__setitem__(key, value) 

Пример использования:

>>> my_dict = CountedOrderedDict({(123,1): 'sda', (232,1) : 'bfd', (234,2) : 'csd', (6745,2) : 'ds', (456,3) : 'rd'}) 
>>> my_dict.counter 
Counter({'1': 2, '2': 2, '3': 1}) 
>>> del my_dict[(123,1)] 
>>> my_dict.counter 
Counter({'2': 2, '1': 1, '3': 1}) 
>>> my_dict[(150,1)] = "asdf" 
>>> my_dict.counter 
Counter({'1': 2, '2': 2, '3': 1}) 

Вот более общая реализация CountedOrderedDict, которая выполняет ключевую функцию в качестве параметра.

import collections 

class CountedOrderedDict(collections.OrderedDict): 
    def __init__(self, key=lambda k: k, *args, **kwargs): 
     self.counter = collections.Counter() 
     self.key_transform = key 
     super(CountedOrderedDict, self).__init__(*args, **kwargs) 

    def __delitem__(self, key): 
     super(CountedOrderedDict, self).__delitem__(key) 
     self.counter[self.key_transform(key)] -= 1 

    def __setitem__(self, key, value): 
     if key not in self: 
      self.counter[self.key_transform(key)] += 1 

     super(CountedOrderedDict, self).__setitem__(key, value) 

Для ваших потребностей, вы бы его экземпляра так:

my_dict = CountedOrderedDict(key=lambda k: k[1]) 
+0

Хорошая точка, плюс этот синтаксис немного чище. :) –

+0

В обоих классах я предлагаю, чтобы '__delitem__' должен ререйзить исключение, которое он ловит, а не подавлять его. Самый простой способ сделать это - просто написать вызов 'super' и декремент без каких-либо блоков' try'/'except'. Любое исключение, возникшее в 'super() .__ delitem__', остановит декремент! В методе '__init__' второго класса в Python 3 вы, вероятно, захотите сделать« ключ »только аргументом ключевого слова, переместив его после' * args'. Таким образом, вы действительно можете передавать позиционные аргументы, не требуя сначала «ключа». Я также предлагаю использовать другое имя, чем 'key'! – Blckknght

+0

Все хорошие предложения! Сейчас отредактирует. –

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