Рубрики
Без рубрики

Получить максимальную отдачу от коллекций Python

Руководство по пониманию, генераторам и полезным функциям и классам. Tagged с Python, коллекциями, советами.

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

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

Список, словарь и установление понимания являются мощной особенностью языка Python, которая позволяет вам объявлять эти типы сбора и одновременно наполнять их значениями. Это позволяет выполнять всевозможные операции фильтрации и картирования простым и читаемым способом.

Список понимания

Без понимания общий шаблон, используемый для создания списка и заполнения его значениями, заключается в следующем:

cubes = []
for i in range(20):
  cubes.append(i ** 3)

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

Это можно сократить до следующего, используя понимание списка:

cubes = [i ** 3 for i in range(20)]

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

Мы знаем, что цель кода состоит в том, чтобы создать список кубических номеров. Поэтому у нас есть задание, использующее квадратные скобки, чтобы показать, что мы создаем список. Внутри понимания мы можем начать со второй половины: для я в диапазоне (20) . Это стандарт для цикла, но вместо тела мы ставим выражение перед ним, давая элемент, который мы создадим на каждой итерации. В этом случае у нас есть я ** 3 , кубивая целевую переменную i . Выражение может быть простым или может включать любые виды функций вызовов или операций, которые вам нравятся. Он даже не должен использовать целевую переменную.

Мы также можем добавить предложение, если в конце фильтровать список:

odd_cubes = [i ** 3 for i in range(20) if i % 2 != 0]

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

Словарь понимание

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

squares = {x: x ** 2 for x in range(100)}

Это создает словарь, отображающие числа, с их квадратными значениями. Как видите, вы устанавливаете ключи и значения, а также итерация в последовательности. Опять же, вы можете использовать, если положения в вашем словарном понимании для выполнения фильтрации одновременно.

Фильтрация будет выглядеть так:

even_squares = {x: x ** 2 for x in range(100) if x % 2 == 0}

Установить понимание

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

square_roots = {math.sqrt(number) for number in number_list}

Опять их можно отфильтровать. Если вы хотите отфильтровать, используя выражение IF, то положение условного в понимании изменяется так:

positive_square_roots = {math.sqrt(number) if number > 0 else 0 for number in number_list}

Более сложные понимания

Понимание также может заменить вложенные для петли. Возьмите пример сглаживания списка списков:

list_of_lists = [[1, 2], [3, 4]]
flat = []
for sub_list in list_of_lists:
  for element in sub_list:
    flat.append(element)

Это производит [1, 2, 3, 4] . Как понимание, которое превращается в:

list_of_lists = [[1, 2], [3, 4]]
flat = [element for sub_list in list_of_lists for element in sub_list]

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

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

Генератор – это особый тип функции Python, которая возвращает ленивый итератор. Вы можете зацикливаться на этих результатах, без того, чтобы весь список их пришлось хранить в памяти.

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

def fib():
    a, b = 0, 1
    while 1:
       yield b
       a, b = b, a + b

Затем вы можете назвать это с помощью кода, как это:

def print_fibonacci(max_size)
  for number in fib():
    if number < max_size:
      print(number)
    else:
      break

Так что здесь происходит? С точки зрения вызовой функции fib () Просто выглядит как коллекция. Когда fib () Сначала вызывает его как обычно, пока не попадет в урожай b утверждение. Затем он возвращает управление своему вызывающему наряду со значением, как возврат b сделал бы. Однако fib () был только приостановлен, и все его состояние было спасено. Когда итератор, который он возвращает, снова запрашивается, он возобновляется с того места, где он остановился, вместо того, чтобы начать снова с самого начала, как обычная функция.

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

cubes = (i ** 3 for i in range(20))

Они возвращают итератор, а не коллекцию. Если итератор исчерпан, и вы попросите его о другом предмете, он вернет Остановка исключение.

На самом деле понимание ведет себя как обертывание выражения генератора при вызове список () или set () :

cubes = list(i ** 3 for i in range(20))

Обратите внимание, что при передаче генератора в функцию Python позволяет вам опустить одну из пар кронштейнов (как в примере прямо выше – мы делаем вызов list () ).

Один совет – это такие функции, как любой , все и сумма Позвольте вам пройти в генератор, а не коллекцию. Это может привести к улучшению производительности из -за ленивой оценки (т. Е. Функция может быть в состоянии вернуться перед оценкой всех элементов из генератора).

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

Возьмите пример чтения строк из файла. Если файл большой, то чтение всего его в памяти может быть чрезвычайно медленным или невозможным. Объект файла, возвращенный open () уже является генератором линий, поэтому можно использовать так:

def read_lines(file):
  with open(file) as f:
    for line in f:
      print(line)

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

DefaultDict

Это подкласс словаря, который называет заводскую функцию для предоставления пропущенных значений.

Это позволяет заменить это:

fruit_sizes = {}
for fruit in fridge:
  if fruit.name in fruit_sizes:
    fruit_sizes[fruit.name].append(fruit.size)
  else:
    fruit_sizes[fruit.name] = [fruit.size]

с этим:

fruit_sizes = defaultdict(list)
for fruit in fridge:
  fruit_sizes[fruit.name].append(fruit.size)

Здесь любые пропущенные значения инициализируются в пустых списках. Это избегает загромождения кода с проверками и инициализацией.

Другие распространенные аргументы – создавать DefaultDicts из int , плавать , диктат или установить типы

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

student_hats = defaultdict([Hat('green', 'small')])

Прилавок

Необходимо подсчитать происшествие множества различных объектов с хранением? A Счетчик твой друг. Это словарный подкласс, где элементы являются ключами, а их количество – это значения.

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

Легкий способ начать понимание Счетчик это подсчитать все случаи строк в списке.

counts = Counter(['Fred', 'Samantha', 'Jean-Claude', 'Samantha'])

счета Тогда выглядит так:

Counter({'Samantha': 2, 'Fred': 1, 'Jean-Claude': 1})

Затем вы можете обновить Счетчик с дальнейшими итерами или другими случаями Прилавок .

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

Используя любой () и все ()

Обычным шаблоном для того, чтобы установить что -то о коллекции, является следующее:

any_green = False
for hat in hats:
  if hat.colour == 'green':
    any_green = True
    break

Это можно заменить на вызов любой () Использование генератора:

any_green = any(hat.colour == 'green' for hat in hats)

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

All () ведет себя одинаково, но возвращает, удовлетворяют ли все элементы.

Итерация над коллекциями вместе с Zip ()

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

for name, email in zip(names, emails):
  create_person(name, email)

Это проходит через обе коллекции одновременно, возвращая кортеж, состоящий из элементов из каждого. Здесь мы распаковали кортеж в отдельные значения. Zip () может взять столько коллекций, сколько захотите, но остановится, когда Самый короткий измотан. Если вы хотите справиться со всеми значениями, вы можете использовать itertools.zip_longest (*iterables,) Анкет Это будет продолжаться до тех пор, пока все коллекции не будут исчерпаны, заполняя недостающие значения FillValue Анкет

Одно изящное использование Zip () Функции должны итерации над парами элементов в той же коллекции. Например:

differences = [next_elt - elt for elt, next_elt in zip(items, items[1:])]

Цепование нескольких коллекций

Если вам нужно итерацию по нескольким коллекциям по одному, вы можете использовать itertools.chain () Анкет

for name in itertools.chain(first_name_list, second_name_list):
  create_person(name)

Это итерация через первую коллекцию, пока не исчерпана , затем переходит к следующему и так далее.

Есть намного более интересные и продвинутые функции в итулс Модуль, и мы вернемся к ним в еще один пост в блоге.

Оригинал: “https://dev.to/sourcery/getting-the-most-out-of-python-collections-3o0c”