Как применить функцию ко всем элементам списка пайтон
Перейти к содержимому

Как применить функцию ко всем элементам списка пайтон

  • автор:

Списки (list). Функции и методы списков

Python 3 логотип

Сегодня я расскажу о таком типе данных, как списки, операциях над ними и методах, о генераторах списков и о применении списков.

Что такое списки?

Списки в Python — упорядоченные изменяемые коллекции объектов произвольных типов (почти как массив, но типы могут отличаться).

Чтобы использовать списки, их нужно создать. Создать список можно несколькими способами. Например, можно обработать любой итерируемый объект (например, строку) встроенной функцией list:

Список можно создать и при помощи литерала:

Как видно из примера, список может содержать любое количество любых объектов (в том числе и вложенные списки), или не содержать ничего.

И еще один способ создать список — это генераторы списков. Генератор списков — способ построить новый список, применяя выражение к каждому элементу последовательности. Генераторы списков очень похожи на цикл for.

Возможна и более сложная конструкция генератора списков:

Но в сложных случаях лучше пользоваться обычным циклом for для генерации списков.

Функции и методы списков

Создать создали, теперь нужно со списком что-то делать. Для списков доступны основные встроенные функции, а также методы списков.

Таблица «методы списков»

Метод Что делает
list.append(x) Добавляет элемент в конец списка
list.extend(L) Расширяет список list, добавляя в конец все элементы списка L
list.insert(i, x) Вставляет на i-ый элемент значение x
list.remove(x) Удаляет первый элемент в списке, имеющий значение x. ValueError, если такого элемента не существует
list.pop([i]) Удаляет i-ый элемент и возвращает его. Если индекс не указан, удаляется последний элемент
list.index(x, [start [, end]]) Возвращает положение первого элемента со значением x (при этом поиск ведется от start до end)
list.count(x) Возвращает количество элементов со значением x
list.sort([key=функция]) Сортирует список на основе функции
list.reverse() Разворачивает список
list.copy() Поверхностная копия списка
list.clear() Очищает список

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

   И, напоследок, примеры работы со списками:

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

Для вставки кода на Python в комментарий заключайте его в теги

  • Модуль csv - чтение и запись CSV файлов
  • Создаём сайт на Django, используя хорошие практики. Часть 1: создаём проект
  • Онлайн-обучение Python: сравнение популярных программ
  • Книги о Python
  • GUI (графический интерфейс пользователя)
  • Курсы Python
  • Модули
  • Новости мира Python
  • NumPy
  • Обработка данных
  • Основы программирования
  • Примеры программ
  • Типы данных в Python
  • Видео
  • Python для Web
  • Работа для Python-программистов
  • Сделай свой вклад в развитие сайта!
  • Самоучитель Python
  • Карта сайта
  • Отзывы на книги по Python
  • Реклама на сайте

Списки, кортежи и словари

Для работы с наборами данных Python предоставляет такие встроенные типы как списки, кортежи и словари.

Список (list) представляет тип данных, который хранит набор или последовательность элементов. Во многих языках программирования есть аналогичная структура данных, которая называется массив.

Создание списка

Для создания списка применяются квадратные скобки [] , внутри которых через запятую перечисляются элементы списка. Например, определим список чисел:

numbers = [1, 2, 3, 4, 5]

Подобным образом можно определять списки с данными других типов, например, определим список строк:

people = ["Tom", "Sam", "Bob"]

Также для создания списка можно использовать функцию-конструктор list() :

numbers1 = [] numbers2 = list()

Оба этих определения списка аналогичны - они создают пустой список.

Список необязательно должен содержать только однотипные объекты. Мы можем поместить в один и тот же список одновременно строки, числа, объекты других типов данных:

objects = [1, 2.6, "Hello", True]

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

numbers = [1, 2, 3, 4, 5] people = ["Tom", "Sam", "Bob"] print(numbers) # [1, 2, 3, 4, 5] print(people) # ["Tom", "Sam", "Bob"]

Конструктор list может принимать набор значений, на основе которых создается список:

numbers1 = [1, 2, 3, 4, 5] numbers2 = list(numbers1) print(numbers2) # [1, 2, 3, 4, 5] letters = list("Hello") print(letters) # ['H', 'e', 'l', 'l', 'o']

Если необходимо создать список, в котором повторяется одно и то же значение несколько раз, то можно использовать символ звездочки *, то есть фактически применить операцию умножения к уже существующему списку:

numbers = [5] * 6 # 6 раз повторяем 5 print(numbers) # [5, 5, 5, 5, 5, 5] people = ["Tom"] * 3 # 3 раза повторяем "Tom" print(people) # ["Tom", "Tom", "Tom"] students = ["Bob", "Sam"] * 2 # 2 раза повторяем "Bob", "Sam" print(students) # ["Bob", "Sam", "Bob", "Sam"]

Обращение к элементам списка

Для обращения к элементам списка надо использовать индексы, которые представляют номер элемента в списка. Индексы начинаются с нуля. То есть первый элемент будет иметь индекс 0, второй элемент - индекс 1 и так далее. Для обращения к элементам с конца можно использовать отрицательные индексы, начиная с -1. То есть у последнего элемента будет индекс -1, у предпоследнего - -2 и так далее.

people = ["Tom", "Sam", "Bob"] # получение элементов с начала списка print(people[0]) # Tom print(people[1]) # Sam print(people[2]) # Bob # получение элементов с конца списка print(people[-2]) # Sam print(people[-1]) # Bob print(people[-3]) # Tom

Для изменения элемента списка достаточно присвоить ему новое значение:

people = ["Tom", "Sam", "Bob"] people[1] = "Mike" # изменение второго элемента print(people[1]) # Mike print(people) # ["Tom", "Mike", "Bob"]

Разложение списка

Python позволяет разложить список на отдельные элементы:

people = ["Tom", "Bob", "Sam"] tom, bob, sam = people print(tom) # Tom print(bob) # Bob print(sam) # Sam

В данном случае переменным tom, bob и sam последовательно присваиваются элементы из списка people. Однако следует учитывать, что количество переменных должно быть равно числу элементов присваиваемого списка.

Перебор элементов

Для перебора элементов можно использовать как цикл for, так и цикл while.

Перебор с помощью цикла for :

people = ["Tom", "Sam", "Bob"] for person in people: print(person)

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

Перебор также можно сделать с помощью цикла while :

people = ["Tom", "Sam", "Bob"] i = 0 while i < len(people): print(people[i]) # применяем индекс для получения элемента i += 1

Для перебора с помощью функции len() получаем длину списка. С помощью счетчика i выводит по элементу, пока значение счетчика не станет равно длине списка.

Сравнение списков

Два списка считаются равными, если они содержат один и тот же набор элементов:

numbers1 = [1, 2, 3, 4, 5] numbers2 = list([1, 2, 3, 4, 5]) if numbers1 == numbers2: print("numbers1 equal to numbers2") else: print("numbers1 is not equal to numbers2")

В данном случае оба списка будут равны.

Получение части списка

Если необходимо получить какую-то определенную часть списка, то мы можем применять специальный синтаксис, который может принимать следующие формы:

  • list[:end] : через параметр end передается индекс элемента, до которого нужно копировать список
  • list[start:end] : параметр start указывает на индекс элемента, начиная с которого надо скопировать элементы
  • list[start:end:step] : параметр step указывает на шаг, через который будут копироваться элементы из списка. По умолчанию этот параметр равен 1.
people = ["Tom", "Bob", "Alice", "Sam", "Tim", "Bill"] slice_people1 = people[:3] # с 0 по 3 print(slice_people1) # ["Tom", "Bob", "Alice"] slice_people2 = people[1:3] # с 1 по 3 print(slice_people2) # ["Bob", "Alice"] slice_people3 = people[1:6:2] # с 1 по 6 с шагом 2 print(slice_people3) # ["Bob", "Sam", "Bill"]

Можно использовать отрицательные индексы, тогда отсчет будет идти с конца, например, -1 - предпоследний, -2 - третий сконца и так далее.

people = ["Tom", "Bob", "Alice", "Sam", "Tim", "Bill"] slice_people1 = people[:-1] # с предпоследнего по нулевой print(slice_people1) # ["Tom", "Bob", "Alice", "Sam", "Tim"] slice_people2 = people[-3:-1] # с третьего с конца по предпоследний print(slice_people2) # [ "Sam", "Tim"]

Методы и функции по работе со списками

Для управления элементами списки имеют целый ряд методов. Некоторые из них:

  • append(item) : добавляет элемент item в конец списка
  • insert(index, item) : добавляет элемент item в список по индексу index
  • extend(items) : добавляет набор элементов items в конец списка
  • remove(item) : удаляет элемент item. Удаляется только первое вхождение элемента. Если элемент не найден, генерирует исключение ValueError
  • clear() : удаление всех элементов из списка
  • index(item) : возвращает индекс элемента item. Если элемент не найден, генерирует исключение ValueError
  • pop([index]) : удаляет и возвращает элемент по индексу index. Если индекс не передан, то просто удаляет последний элемент.
  • count(item) : возвращает количество вхождений элемента item в список
  • sort([key]) : сортирует элементы. По умолчанию сортирует по возрастанию. Но с помощью параметра key мы можем передать функцию сортировки.
  • reverse() : расставляет все элементы в списке в обратном порядке
  • copy() : копирует список

Кроме того, Python предоставляет ряд встроенных функций для работы со списками:

  • len(list) : возвращает длину списка
  • sorted(list, [key]) : возвращает отсортированный список
  • min(list) : возвращает наименьший элемент списка
  • max(list) : возвращает наибольший элемент списка

Добавление и удаление элементов

Для добавления элемента применяются методы append() , extend и insert , а для удаления - методы remove() , pop() и clear() .

people = ["Tom", "Bob"] # добавляем в конец списка people.append("Alice") # ["Tom", "Bob", "Alice"] # добавляем на вторую позицию people.insert(1, "Bill") # ["Tom", "Bill", "Bob", "Alice"] # добавляем набор элементов ["Mike", "Sam"] people.extend(["Mike", "Sam"]) # ["Tom", "Bill", "Bob", "Alice", "Mike", "Sam"] # получаем индекс элемента index_of_tom = people.index("Tom") # удаляем по этому индексу removed_item = people.pop(index_of_tom) # ["Bill", "Bob", "Alice", "Mike", "Sam"] # удаляем последний элемент last_item = people.pop() # ["Bill", "Bob", "Alice", "Mike"] # удаляем элемент "Alice" people.remove("Alice") # ["Bill", "Bob", "Mike"] print(people) # ["Bill", "Bob", "Mike"] # удаляем все элементы people.clear() print(people) # []

Проверка наличия элемента

Если определенный элемент не найден, то методы remove и index генерируют исключение. Чтобы избежать подобной ситуации, перед операцией с элементом можно проверять его наличие с помощью ключевого слова in :

people = ["Tom", "Bob", "Alice", "Sam"] if "Alice" in people: people.remove("Alice") print(people) # ["Tom", "Bob", "Sam"]

Выражение if "Alice" in people возвращает True, если элемент "Alice" имеется в списке people. Поэтому конструкция if "Alice" in people может выполнить последующий блок инструкций в зависимости от наличия элемента в списке.

Удаление с помощью del

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

people = ["Tom", "Bob", "Alice", "Sam", "Bill", "Kate", "Mike"] del people[1] # удаляем второй элемент print(people) # ["Tom", "Alice", "Sam", "Bill", "Kate", "Mike"] del people[:3] # удаляем по четвертый элемент не включая print(people) # ["Bill", "Kate", "Mike"] del people[1:] # удаляем со второго элемента print(people) # ["Bill"]

Изменение подсписка

Для изменения подсписка - набора элементов в списке можно использовать вышерассмотренный синтаксис [start:end] :

nums = [10, 20, 30, 40, 50] nums[1:4]=[11, 22] print(nums) # [10, 11, 22, 50]

Здесь выражение nums[1:4] фактически обращается к подсписку [20, 30, 40] . Присвоение этому подсписку списка [11, 22] позволяет заменить элемента с 1 по 4 индекс не включая на элементы [11, 22] . И после изменения получим список [10, 11, 22, 50]

Подсчет вхождений

Если необходимо узнать, сколько раз в списке присутствует тот или иной элемент, то можно применить метод count() :

people = ["Tom", "Bob", "Alice", "Tom", "Bill", "Tom"] people_count = people.count("Tom") print(people_count) # 3

Сортировка

Для сортировки по возрастанию применяется метод sort() :

people = ["Tom", "Bob", "Alice", "Sam", "Bill"] people.sort() print(people) # ["Alice", "Bill", "Bob", "Sam", "Tom"]

Если необходимо отсортировать данные в обратном порядке, то мы можем после сортировки применить метод reverse() :

people = ["Tom", "Bob", "Alice", "Sam", "Bill"] people.sort() people.reverse() print(people) # ["Tom", "Sam", "Bob", "Bill", "Alice"]

При сортировке фактически сравниваются два объекта, и который из них "меньше", ставится перед тем, который "больше". Понятия "больше" и "меньше" довольно условны. И если для чисел все просто - числа расставляются в порядке возрастания, то для строк и других объектов ситуация сложнее. В частности, строки оцениваются по первым символам. Если первые символы равны, оцениваются вторые символы и так далее. При чем цифровой символ считается "меньше", чем алфавитный заглавный символ, а заглавный символ считается меньше, чем строчный.

Таким образом, если в списке сочетаются строки с верхним и нижним регистром, то мы можем получить не совсем корректные результаты, так как для нас строка "bob" должна стоять до строки "Tom". И чтобы изменить стандартное поведение сортировки, мы можем передать в метод sort() в качестве параметра функцию:

people = ["Tom", "bob", "alice", "Sam", "Bill"] people.sort() # стандартная сортировка print(people) # ["Bill", "Sam", "Tom", "alice", "bob"] people.sort(key=str.lower) # сортировка без учета регистра print(people) # ["alice", "Bill", "bob", "Sam", "Tom"]

Кроме метода sort мы можем использовать встроенную функцию sorted , которая имеет две формы:

  • sorted(list) : сортирует список list
  • sorted(list, key) : сортирует список list, применяя к элементам функцию key
people = ["Tom", "bob", "alice", "Sam", "Bill"] sorted_people = sorted(people, key=str.lower) print(sorted_people) # ["alice", "Bill", "bob", "Sam", "Tom"]

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

Фильтрация списка

Для фильтрации списка применяется функция filter() , в которую передается функция-условие и фильтруемый список:

filter(fun, iter)

Функция принимает два параметра:

  • fun : функция-условие, в которую передается каждый элемент коллекции и которая возвращает True, если элемент соответствует условию. Иначе возвращается False.
  • iter : фильтруемая коллекция

В качестве результата функция возвращает список отфильтрованных элементов. Например, получим из списка чисел все значения больше 1:

numbers = [-5, -4, -3 ,-2, -1, 0, 1, 2, 3, 4, 5] def condition(number): return number > 1 result = filter(condition, numbers) for n in result: print(n, end=" ") # 2 3 4 5

Здесь фильтруется список numbers. Для фильтрации определяем функцию condition, в которую в качестве параметра передается каждый элемент списка numbers. Результатом функции являет True , если число больше 1, либо False , если число меньше 2.

Результатом функции filter является отфильтрованный список.

Вместо определения отдельной функции-условия, если условие короткое, удобно в подобных случаях использовать лямбда-выражения:

numbers = [-5, -4, -3 ,-2, -1, 0, 1, 2, 3, 4, 5] result = filter(lambda n: n > 1, numbers) for n in result: print(n, end=" ") # 2 3 4 5

Аналогичным образом можно отфильтровать списки более сложных объектов:

class Person: def __init__(self, name, age): self.name = name self.age = age people = [ Person("Tom", 38), Person("Kate", 31), Person("Bob", 42), Person("Alice", 34), Person("Sam", 25) ] # фильтрация элементов, у которых age > 33 view = filter(lambda p: p.age > 33, people) for person in view: print(f"Name: Age: ")

В данном случае фильтруем список объектов Person, поэтому в функцию-условие/лямбда-выражение в качестве параметра передается каждый объект Person из списка. Каждый объект Person хранит имя (Name) и возраст (Age), и здесь выбираем всех Person, у которых возраст больше 33.

Проекция списка

Для проекции/преобразования элементов списка применяется функция map() , в которую передается функция-условие и фильтруемый список:

map(fun, iter)

Функция принимает два параметра:

  • fun : функция преобразования, в которую передается каждый элемент коллекции.
  • iter : перебираемая коллекция

В качестве результата функция возвращает список значений, которые получены после применения функции преобразования. Например, преобразуем список чисел в список их квадратов:

numbers = [ 1, 2, 3, 4, 5] def square(number): return number * number result = map(square, numbers) for n in result: print(n, end=" ") # 1 4 9 16 25

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

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

numbers = [ 1, 2, 3, 4, 5] result = map(lambda n: n * n, numbers) for n in result: print(n, end=" ") # 1 4 9 16 25

Аналогичным образом можно преобразовывать коллекции более сложных объектов:

class Person: def __init__(self, name, age): self.name = name self.age = age people = [ Person("Tom", 38), Person("Kate", 31), Person("Bob", 42), Person("Alice", 34), Person("Sam", 25) ] # получаем из Person строку с именем view = map(lambda p: p.name, people) for person in view: print(person)

Здесь проекция применяется к списку объектов Person. Функция преобразование получает каждый объект Person и возвращает значение его атрибута name. То есть полученный в результате список будет содержать набор строк (атрибуты name всех объектов Person). Консольный вывод:

Tom Kate Bob Alice Sam

Минимальное и максимальное значения

Встроенный функции Python min() и max() позволяют найти минимальное и максимальное значения соответственно:

numbers = [9, 21, 12, 1, 3, 15, 18] print(min(numbers)) # 1 print(max(numbers)) # 21

Копирование списков

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

people1 = ["Tom", "Bob", "Alice"] people2 = people1 people2.append("Sam") # добавляем элемент во второй список # people1 и people2 указывают на один и тот же список print(people1) # ["Tom", "Bob", "Alice", "Sam"] print(people2) # ["Tom", "Bob", "Alice", "Sam"]

Это так называемое "поверхностное копирование" (shallow copy). И, как правило, такое поведение нежелательное. И чтобы происходило копирование элементов, но при этом переменные указывали на разные списки, необходимо выполнить глубокое копирование (deep copy). Для этого можно использовать метод copy() :

people1 = ["Tom", "Bob", "Alice"] people2 = people1.copy() # копируем элементы из people1 в people2 people2.append("Sam") # добавляем элемент ТОЛЬКО во второй список # people1 и people2 указывают на разные списки print(people1) # ["Tom", "Bob", "Alice"] print(people2) # ["Tom", "Bob", "Alice", "Sam"]

Соединение списков

Для объединения списков применяется операция сложения (+):

people1 = ["Tom", "Bob", "Alice"] people2 = ["Tom", "Sam", "Tim", "Bill"] people3 = people1 + people2 print(people3) # ["Tom", "Bob", "Alice", "Tom", "Sam", "Tim", "Bill"]

Списки списков

Списки кроме стандартных данных типа строк, чисел, также могут содержать другие списки. Подобные списки можно ассоциировать с таблицами, где вложенные списки выполняют роль строк. Например:

people = [ ["Tom", 29], ["Alice", 33], ["Bob", 27] ] print(people[0]) # ["Tom", 29] print(people[0][0]) # Tom print(people[0][1]) # 29

Чтобы обратиться к элементу вложенного списка, необходимо использовать пару индексов: people[0][1] - обращение ко второму элементу первого вложенного списка.

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

people = [ ["Tom", 29], ["Alice", 33], ["Bob", 27] ] # создание вложенного списка person = list() person.append("Bill") person.append(41) # добавление вложенного списка people.append(person) print(people[-1]) # ["Bill", 41] # добавление во вложенный список people[-1].append("+79876543210") print(people[-1]) # ["Bill", 41, "+79876543210"] # удаление последнего элемента из вложенного списка people[-1].pop() print(people[-1]) # ["Bill", 41] # удаление всего последнего вложенного списка people.pop(-1) # изменение первого элемента people[0] = ["Sam", 18] print(people) # [ ["Sam", 18], ["Alice", 33], ["Bob", 27]]

Перебор вложенных списков:

people = [ ["Tom", 29], ["Alice", 33], ["Bob", 27] ] for person in people: for item in person: print(item, end=" | ")
Tom | 29 | Alice | 33 | Bob | 27 |
  • Вопросы для самопроверки
  • Упражнения для самопроверки

Применить функцию к каждому элементу массива

Пример использования функции upper() к каждому элементу массива.

List = ['Python', 'Django'] [x.upper() for x in List] # ['PYTHON', 'DJANGO'] 

Обновлено: 01 ноября 2020

Комментарии

Авторизуйтесь, чтобы добавлять комментарии

  • Список, кортеж
  • Добавить элемент в список
  • Удалить элемент в списке
  • Размер списка и кортежа
  • Суммировать числа в списке
  • Сколько раз встречается элемент в списке
  • Применить функцию к каждому элементу массива
  • Сортировать список
  • Разбить текст на список
  • Минимальное и максимальное значение элемента списка или кортежа

Как применить функцию ко всем элементам списка (произвольной вложенности)

Отвечая на данный вопрос, я заинтересовался более универсальным решением. Есть список произвольной вложенности, например:

['1','2', ['1',['2','4',['5','6']]],'7','8'] 

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

[1, 4, [1, [4, 16, [25, 36]]], 49, 64] 

Я опубликовал свой вариант решения, но мне было бы интересно увидеть альтернативные (более интересные) решения.

Отслеживать
задан 27 дек 2016 в 22:43
MaxU - stand with Ukraine MaxU - stand with Ukraine
149k 12 12 золотых знаков 59 59 серебряных знаков 132 132 бронзовых знака

4 ответа 4

Сортировка: Сброс на вариант по умолчанию

Чтобы поместу изменить, не создавая новые списки (поиск в глубину—depth-first search (DFS)):

def apply_nested(func, lst, isatom=lambda item: not isinstance(item, list)): for i, item in enumerate(lst): if isatom(item): lst[i] = func(item) else: apply_nested(func, item, isatom) 

Здесь isatom() предикат определяет, что является неразрывным элементом (атомом) для заданного алгоритма: apply_nested(func, lst) вызывает func функцию для каждого атома в (глубоковложенном) списке lst . Похожее решение: flatten_gen() .

def apply_nested(func, lst, isatom=lambda item: not isinstance(item, list)): stack = [lst] while stack: lst = stack.pop() for i, item in enumerate(lst): if isatom(item): lst[i] = func(item) else: stack.append(item) 
>>> nested = ['1','2', ['1',['2','4',['5','6']]],'7','8'] >>> apply_nested(lambda atom: int(atom)**2, nested) >>> nested [1, 4, [1, [4, 16, [25, 36]]], 49, 64] 

Аналогично, можно определить функции, которые возвращают новые значения, не изменяя ввода (DFS):

def map_nested(func, lst, isatom=lambda item: not isinstance(item, list)): return [func(item) if isatom(item) else map_nested(func, item, isatom) for item in lst] 
def map_nested(func, lst, isatom=lambda item: not isinstance(item, list)): result = [] stack = [(lst, result)] while stack: lst, new_lst = stack.pop() for item in lst: if isatom(item): new_lst.append(func(item)) else: # item is a sublist (collection) sublist = [] new_lst.append(sublist) stack.append((item, sublist)) return result 
>>> map_nested(lambda atom: int(atom)**2, nested)) [1, 4, [1, [4, 16, [25, 36]]], 49, 64] 

Отслеживать
ответ дан 28 дек 2016 в 18:34
52.3k 11 11 золотых знаков 110 110 серебряных знаков 312 312 бронзовых знаков

Сложно что-то выдумать - в вашем решении уже вроде все есть. Однако, можно попробовать внести пару (ненужных?) изменений + функция в одну строку. Большую функцию тоже можно при желании разместить в 1 строку, однако это уже будет чересчур:

import timeit def map_nested(lst, func=lambda x: x, forbidden_types=(str, int)): container_type = type(lst) if hasattr(lst, '__iter__') and type(lst) not in forbidden_types: return container_type(map_nested(item, func) for item in lst) else: try: return func(lst) except: return lst def map_nested_1line(lst, func=lambda x: x): return [map_nested_1line(item, func) for item in lst] if type(lst) is list else func(lst) 

UPDATE: Утро вечера мудренее и подумав я родил нерекурсивный вариант этой функции. Нерекурсивный хорош тем, что не упадет на длинных и сильно вложенных списках на ОС с ограничением на длину рекурсии. Плох тем, что очень медленный. Суть нерекурсивного решения в том, что в специальный массив заносится порядок входа и выхода во вложенные списки. Находим вложенный массив - заносим указатель на него + индекс в спец. хранилище. Выходим из него - указатель и индекс извлекаем.

# Also non-recursive. Yay! def map_nested_inplace(lst, func=lambda x: x, forbidden_types=(str, int)): # forbidden_types не используется processed_elements = 0 # assert type(lst) is list # blah-blah current_container = lst containers_repo = [[current_container, 0]] # До тех пор, пока не были обработаны все списки и под-списки while len(containers_repo) != 0: while True: try: # Следующий элемент в текущем списке. # Может быть как числом, так и новым под-списком next_one = current_container[containers_repo[-1][1]] break # Исключение означает, что под-список кончился. # Т.к. он кончился, то убираем его из хранилища # и пробуем извлечь следующий элемент except IndexError: containers_repo.pop() if len(containers_repo) == 0: break current_container = containers_repo[-1][0] if len(containers_repo) != 0: if type(next_one) is list: # Это под-список, а не число. # Заносим под-список в хранилище и # на следующей итерации открываем уже его containers_repo[-1][1] += 1 current_container = next_one containers_repo.append([current_container, 0]) else: current_container[containers_repo[-1][1]] = func(current_container[containers_repo[-1][1]]) containers_repo[-1][1] += 1 # ради небольшой проверки processed_elements += 1 # set также работает, но непохоже, чтобы он сохранял порядок lst = ['1', 'an error', ('1', ['2', '4', (5, '6')]), '7', 8] lst_simple = ['1', '2', ['1', ['2', '4', ['5', '6']]], '7', '8'] lst_another_simple = ['1', '2', ['1', ['2', '4', ['5', '6']], ['6', '6', '6'], ['8', '8']], '7', ['11'], '8'] print(map_nested(lst, lambda x: int(x)**2)) print(map_nested_1line(lst_simple, lambda x: int(x)**2)) print(map_nested_1line([], lambda x: int(x)**2)) map_nested_inplace(lst_another_simple, lambda x: int(x)**2) print(lst_another_simple) 

Также немного тестирования:

import random test_array = [] container_tree = [test_array] current_container = container_tree[-1] TOTAL_AMOUNT = 10000 NEW_LEVEL_PROBABILITY = 0.5 for i in range(TOTAL_AMOUNT): if random.random() >= NEW_LEVEL_PROBABILITY: current_container.append([]) container_tree.append(current_container[-1]) current_container = current_container[-1] elif len(container_tree) > 1: current_container = container_tree[-2] current_container.append(str(random.randint(0, 20))) # Для работоспособности рекурсивных методов # Впрочем, без старта новго потока с threading.stack_size() все равно не будет работать :( import sys if sys.getrecursionlimit() < len(container_tree) * 2: sys.setrecursionlimit(len(container_tree) * 2) setup_statement = """from __main__ import test_array, """ # Не используется lambda x**2, потому что # в inplace квадраты будут накатываться до тех пор, # пока хватит памяти - список для каждого прохода должен генерироваться заново print(timeit.timeit("map_nested_inplace(test_array)", setup=setup_statement + "map_nested_inplace", number=100)) print(timeit.timeit("map_nested_1line(test_array)", setup=setup_statement + "map_nested_1line", number=100)) print(timeit.timeit("map_nested(test_array)", setup=setup_statement + "map_nested", number=100)) >>> 1.8096923486838081 # Без рекурсии >>> 0.9477624593712808 # Однострочник >>> 1.827232542223693 # Большая функция 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *