2011-12-19 3 views
7

Что такое лучший способ обновить значение в списке кортежей?Python - обновить значение в списке кортежей

В настоящее время я делаю это как в коде ниже, но я полагаю, что есть более чистый и лаконичный путь.

>>> foo = [('a', 'hello'), ('b', 'world')] 
>>> bar = dict(foo) 
>>> bar['b'] = 'friend' 
>>> foo = bar.items() 
>>> foo 
[('a', 'hello'), ('b', 'friend')] 

Редактировать: причина использования списка кортежей в моем исходном сообщении не ясна. Целью является обновление некоторых значений заголовков приложения wsgi во время обработки ошибок, которые представляют собой список кортежей.

Заранее спасибо.

+4

Зачем вам нужен список кортежей, не Сыроватский? Если порядок важен, для вас есть [OrderedDict] (http://docs.python.org/library/collections.html#collections.OrderedDict). – DrTyrsa

+0

@DrTyrsa Это обновление заголовков приложения wsgi во время обработки ошибок, которые являются списком кортежей – Eric

ответ

6

Ваша структура данных (список кортежей) лучше всего назвать ассоциативным списком. Как было указано, лучше использовать словарь, так как вы получите лучшую амортизированную стоимость операции (вставка, удаление и поиск - это O (1) для словаря, но удаление и поиск - это O (n) для ассоциативного список).

Что касается обновления вашего ассоциативного списка путем преобразования его в словарь, а затем обратно в ассоциативный список, этот метод имеет три недостатка. Это довольно дорого, он может изменить порядок предметов, и он удалит дубликат.

Если вы хотите продолжать использовать ассоциативные списки, вероятно, лучше просто использовать понимание списка для обновления структуры данных.Стоимость будет O (n) во времени и в памяти, но это уже то, что вы имеете при использовании промежуточного словаря.

Вот простой способ сделать это (требуется Python 2.5, потому что использовать тройной оператор):

def update_in_alist(alist, key, value): 
    return [(k,v) if (k != key) else (key, value) for (k, v) in alist] 

def update_in_alist_inplace(alist, key, value): 
    alist[:] = update_in_alist(alist, key, value) 

>>> update_in_alist([('a', 'hello'), ('b', 'world')], 'b', 'friend') 
[('a', 'hello'), ('b', 'friend')] 
+0

Чистый и эффективный код, спасибо! – Eric

6

Поскольку кортежи неизменяемы, вам нужно заменить кортеж новым.

>>> foo[1] = (foo[1][0], "friend") 
>>> foo 
[('a', 'hello'), ('b', 'friend')] 

Конечно, это работает только в том случае, если вы знаете индекс предмета, который хотите заменить. Если все, что у вас есть, это значение первого элемента, то поиск по списку для этого индекса неэффективен, особенно для больших списков. То же самое касается вашего примера выше - преобразование списка в dict и обратно только для изменения нескольких записей не является масштабируемым решением.

Как упоминалось в комментариях к eumiro и DrTysra, если ваша структура данных позволяет это, вам может быть лучше просто использовать dict (или OrderedDict, если заказ важен).

+0

Я не думаю, что индекс известен, только первый элемент в кортеже. – DrTyrsa

+0

@DrTyrsa хороший пункт. –

2

Мне не совсем понятно, чего вы хотите достичь. Кортежи не могут быть изменены, поэтому вы не можете изменить ('b', 'world') на что-то еще. Но вы можете изменить список курсов:

foo[1] = ('b','friend') 

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

+0

Спасибо за ваш ответ, я обновил вопрос. Я хочу обновить некоторые заголовки приложения wsgi во время обработки ошибок. – Eric

3

Из вашего использования, кажется, вы действительно хотите использовать словарь для начала, а не список кортежей. Если вы обрабатываете первое поле каждого кортежа как уникальный ключ, сделайте его словарем, чтобы получить O (1) доступ и проверку того, что ключи действительно уникальны.

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

index = -1 
target = "b" 
new_value = "friend" 
for i, v in enumerate(foo): 
    if v[0] == target: 
    index = i 
    break 
if index >= 0: 
    foo[index] = (foo[index][0], new_value) 

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

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