Изменить все значения в списке списков Python? - PullRequest
4 голосов
/ 29 марта 2009

Допустим, у меня есть список вроде:

my_list = [[1,2,3],[4,5,6],[7,8,9]]

Как мне изменить каждое значение в списке, не делая?:

for x in range(0, 3):
    for y in range(0, 3):
        my_list[x][y] = -my_list[x][y]

Я попытался упростить это, выполнив

my_list = [[[-a, -b, -c] for [a, b, c] in d] for d in my_list]

но значения остаются прежними.

Ответы [ 9 ]

11 голосов
/ 29 марта 2009

Другой вариант - использовать встроенную функцию map:

>>> my_list = [[1,2,3],[4,5,6],[7,8,9]]
>>> neg = lambda x: -x
>>> f = lambda x: map(neg, x)
>>> map(f, my_list)
[[-1, -2, -3], [-4, -5, -6], [-7, -8, -9]]
7 голосов
/ 29 марта 2009

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

Вот моя версия лучшего в своем классе решения для изменения списка мест:

def alter_elements(lst, func):
  for i, item in enumerate(lst):
    if isinstance(item, list):
      alter_elements(item, func)
    else:
      lst[i] = func(item)

Тестовый прогон:

>>> sample = [[1,2,3],[4,5,6],[7,8,9]]
>>> alter_elements(sample, lambda x: -x)
>>> print sample
>>> [[-1, -2, -3], [-4, -5, -6], [-7, -8, -9]]

Нет списка копий. Нет жестких границ. Нет списка понимания с побочными эффектами.

6 голосов
/ 29 марта 2009

Можно закодировать более общее решение этой проблемы. Следующее работает в Python 3.0, независимо от уровня вложенности .

Давайте определим recursive_map:

import collections

def recursive_map(f, iterable):
    for e in iterable:
        if isinstance(e, collections.Iterable):
            yield recursive_map(f, e)
        else:
            yield f(e)

Теперь запрошенная функция отрицания может быть закодирована следующим образом:

import functools
import operator

negate = functools.partial(recursive_map, operator.neg)

Таким образом, для некоторого набора произвольно вложенных итераций x мы вычисляем его отрицание y следующим образом:

y = negate(x)

Добавление:

Как отметил пользователь chradcliffe , вышеуказанная функция negate дает генератор, который может содержать другие генераторы, которые ... и т. Д. Чтобы расширить / оценить все эти генераторы, нам нужно применить list() всем им. Итак, мы определили еще одну общую функцию отображения, на этот раз ту, которая работает над самими итерациями.

def recursive_iter_map(f, iterable):
    def rec(e):
        if isinstance(e, collections.Iterable):
            return recursive_iter_map(f, e)
        else:
            return e

    return f(map(rec, iterable))

Теперь

all_lists = functools.partial(recursive_iter_map, list)
y = all_lists(negate(x))

фактически сразу отрицает каждый элемент и возвращает полный список.

Обратите внимание, что мы можем рассматривать вложенную коллекцию итераций как дерево . Каждое итерируемое - это поддерево, а не итерируемое - это листья. Следовательно, первая определенная мной функция работает с листьями, а вторая - с не листьями.

3 голосов
/ 29 марта 2009

Попробуйте это:

my_list = [[-a, -b, -c] for [a, b, c] in my_list]

Обратите внимание, что это создает новый список, а не изменяет список.

3 голосов
/ 29 марта 2009

Под "alter" я предполагаю, что вы имеете в виду "отрицание" (но вы должны были это сказать).

Я заметил, что вы перебираете каждый элемент двумерного массива (список списков) и рассматриваете каждый элемент как список из трех элементов ... но на самом деле каждый элемент - это просто число в вашем вопрос как заявлено. Поэтому я бы сделал что-то вроде этого:

my_list = [[-n for n in l] for l in my_list]
1 голос
/ 29 марта 2009

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

>>> my_list = [[1,2,3],[4,5,6],[7,8,9]]
>>> [[slist.__setitem__(i, -n) for i, n in enumerate(slist)] for slist in my_list]
[[None, None, None], [None, None, None], [None, None, None]]
>>> my_list
[[-1, -2, -3], [-4, -5, -6], [-7, -8, -9]]

Явные for петли

Использование явных циклов for, как показано в других ответах, считается лучшим стилем, когда возникают побочные эффекты. Я думаю, что хорошая форма для стиля for loop:

for nested_list in my_list:
    for i, x in enumerate(nested_list):
        nested_list[i] = -x
1 голос
/ 29 марта 2009

Хотя ответ Константина верен, я бы сделал два улучшения:

  1. Общность не всегда лучший способ. Что, если функция изменения должна работать со списками?
  2. Использование enumerate и индексация не так быстры, как создание копии списка и назначение ее на месте с использованием [:] срезов.

Итак, вот мой вариант, который также изменяет список и все внутренние списки на месте:

def lst_apply(lst, func, lvl):
    if lvl:
        for x in lst:
            lst_apply(x, func, lvl - 1)
    else:
        lst[:] = [func(x) for x in lst]

>>> lst_apply(my_list, lambda x: -x, 1)
>>> my_list
[[-1, -2, -3], [-4, -5, -6], [-7, -8, -9]]

Я все еще верю, однако, что лучший подход здесь:

def simple_apply(lst, func):
    lst[:] = [[func(x) for x in y] for y in lst]

timeit результаты:

  • simple_apply: 4,0 с
  • lst_apply: 5,4 с
  • alter_elements: 11,5 с
1 голос
/ 29 марта 2009

Как уже отмечали другие, существует два "уровня" списков и один "уровень" целых, поэтому два цикла дают вам int, а не другой список.

Для сравнения, версия цикла должна быть:

for x in range(0, 3):
    for y in range(0, 3):
        my_list[x][y] = -my_list[x][y]

Это также устанавливает границы для вашего диапазона. Верхняя граница является эксклюзивной, не включительно.

0 голосов
/ 29 марта 2009

Если у вас есть массив чисел 3х3, и вы хотите выполнить преобразование для каждого его элемента, я подозреваю, что в долгосрочной перспективе вам может быть лучше обслужено использование числовой библиотеки, такой как NumPy / SciPy, и использование матрицы подпрограммы это обеспечивает. Если вы каким-либо образом заинтересованы в высокой производительности, то это будет необходимо. Большая часть гибкости Python может быть упущена на уровне элементов массива в обмен на оптимизированную скорость численного алгоритма.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...