One place for hosting & domains

      Использование

      Использование функции фильтра Python


      Введение

      Встроенная функция filter()​​​ Python может использоваться для создания нового итератора из существующего итерируемого объекта (например списка или словаря), который эффективно отфильтрует элементы с помощью предоставленной нами функции. Итерируемый объект — это объект Python, по которому можно выполнить итерацию, то есть он вернет элементы в такой последовательности, которую мы можем использовать в цикле for​​​.

      Базовый синтаксис для функции filter():

      filter(function, iterable)
      

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

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

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

      Использование filter() с функцией

      Первый аргумент в filter() — это функция, которую мы используем для решения о включении или фильтрации каждого элемента. Функция вызывается один раз для каждого элемента в итерируемом объекте, переданном как второй аргумент и каждый раз при возвращении False​​​ значение сбрасывается. Поскольку этот аргумент является функцией, мы можем либо передать обычную функцию, либо использовать функции lambda, особенно когда выражение является менее сложным.

      Ниже приводится синтаксис lambda с filter():

      filter(lambda item: item[] expression, iterable)
      

      С помощью списка ниже можно включить функцию lambda с выражением, по которому мы хотим оценить каждый элемент из списка:

      creature_names = ['Sammy', 'Ashley', 'Jo', 'Olly', 'Jackie', 'Charlie']
      

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

      print(list(filter(lambda x: x[0].lower() in 'aeiou', creature_names)))
      

      Здесь мы заявляем в нашем списке элемент в качестве x. Затем мы настроим наше выражение для доступа к первому символу каждой строки (или символу «нуль»), то есть x[0]​​​. Установка нижнего регистра в каждом из названий обеспечит соответствие букв строке в нашем выражении, 'aeiou'​​​.

      Наконец, мы передадим итерируемому объекту creature_names​​. Как и в предыдущем разделе, мы применяем list() к результату, чтобы создать список из возвратов итератора filter()​​​.

      Вывод будет выглядеть следующим образом:

      Output

      ['Ashley', 'Olly']

      Этот же результат можно получить с помощью определяемой нами функции:

      creature_names = ['Sammy', 'Ashley', 'Jo', 'Olly', 'Jackie', 'Charlie']
      
      def names_vowels(x):
        return x[0].lower() in 'aeiou'
      
      filtered_names = filter(names_vowels, creature_names)
      
      print(list(filtered_names))
      

      Наша функция names_vowels определяет выражение, которое мы будем применять для фильтра creature_names​​.

      Снова, вывод будет выглядеть следующим образом:

      Output

      ['Ashley', 'Olly']

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

      Использование None с filter()

      Мы можем передать None в качестве первого аргумента filter(), чтобы исключить из возвращенного фильтра итератора любые значения, которые Python считает «ложными». Обычно Python считает все значения длиной 0​​​ (например пустой список или пустую строку) или числовой эквивалент 0​​​ ложными, отсюда использование термина «ложный».

      В следующем случае мы хотим фильтровать наш список так, чтобы отображались только номера номера емкостей в нашем аквариуме:

      aquarium_tanks = [11, False, 18, 21, "", 12, 34, 0, [], {}]
      

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

      filtered_tanks = filter(None, aquarium_tanks)
      

      Мы используем функцию filter() с None и передадим список aquarium_tanks​​​ в качестве нашего итерируемого объекта. Поскольку мы передали None в качестве первого аргумента, проверим, считаются ли элементы в нашем списке ложными.

      print(list(filtered_tanks))
      

      Затем обернем filtered_tanks​​​​​ в функцию list() для возврата списка для filtered_tanks​​​ при печати.

      Здесь мы видим, что вывод отображает только целые значения. Все элементы, оцененные как False, равные 0 по длине, были удалены filter()​​​:

      Output

      [11, 25, 18, 21, 12, 34]

      Примечание. Если мы не используем list() и печатаем filtered_tanks​​​, мы получим объект фильтра вроде <filter object at 0x7fafd5903240>. Объект фильтра является итерируемым, поэтому мы можем пройтись по нему с помощью for или использовать list()​​​ для превращения его в список, который мы здесь делаем, так как это хороший способ проверки результатов.

      C None мы использовали filter() для быстрого удаления элементов из нашего списка, которые считались ложными.

      Использование filter() со списком словарей

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

      Например, допустим, у нас есть список всех созданий в аквариуме с различными данными о каждом из них:

      aquarium_creatures = [
        {"name": "sammy", "species": "shark", "tank number": "11", "type": "fish"},
        {"name": "ashley", "species": "crab", "tank number": "25", "type": "shellfish"},
        {"name": "jo", "species": "guppy", "tank number": "18", "type": "fish"},
        {"name": "jackie", "species": "lobster", "tank number": "21", "type": "shellfish"},
        {"name": "charlie", "species": "clownfish", "tank number": "12", "type": "fish"},
        {"name": "olly", "species": "green turtle", "tank number": "34", "type": "turtle"}
      ]
      

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

      def filter_set(aquarium_creatures, search_string):
          def iterator_func(x):
              for v in x.values():
                  if search_string in v:
                      return True
              return False
          return filter(iterator_func, aquarium_creatures)
      

      Мы определяем функцию filter_set(), которая принимает aquarium_creatures и search_string в качестве параметров. В filter_set() мы передадим iterator_func() в качестве функции filter(). Функция filter_set() вернет итератор, полученный от filter().

      iterator_func() принимает x в качестве аргумента, представляющего элемент в нашем списке (т.е. единый словарь).

      Затем цикл for​​​ получит доступ к каждой паре key:value в наших словарях, а затем будет использовать условное выражение для проверки того, есть ли search_string в v, представляя значение.

      Как и в предыдущих примерах, если выражение оценивает значение как True, функция добавляет элемент в объект фильтра. Оно вернется после выполнения функции filter_set(). Мы расположим return False​​​ за пределами нашего цикла для проверки каждого элемента в каждом словаре вместо получения результатов после проверки одного первого словаря.

      Мы вызовем filter_set() с нашим списком словарей и строкой поиска, для которых мы хотим найти совпадения:

      filtered_records = filter_set(aquarium_creatures, "2")    
      

      После выполнения функции мы сохраним объект фильтра в переменной filtered_records​​​, которую мы превращаем в список и печатаем:

      print(list(filtered_records))      
      

      Мы увидим следующий вывод из этой программы:

      Output

      [{'name': 'ashley', 'species': 'crab', 'tank number': '25', 'type': 'shellfish'}, {'name': 'jackie', 'species': 'lobster', 'tank number': '21', 'type': 'shellfish'}, {'name': 'charlie', 'species': 'clownfish', 'tank number': '12', 'type': 'fish'}]

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

      Заключение

      В этом обучающем модуле мы узнали о различных способах использования функции filter()​​​ в Python. Теперь вы можете использовать filter() со своей собственной функцией, функцией lambda​​​ или с None для фильтрации элементов структур данных различной сложности.

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

      Для получения дополнительной информации о Python​​ ознакомьтесь с нашей серией Программирование на Python 3 и посетите нашу тематическую страницу, посвященную Python.



      Source link

      Установка и использование Docker Compose в Ubuntu 20.04


      Введение

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

      Если приложение использует несколько служб, организация совместного запуска, взаимодействия и отключения всех контейнеров быстро станет неудобной. Docker Compose — инструмент, позволяющий запускать среды приложений с несколькими контейнерами на основе определений, задаваемых в файле YAML. Он использует определения служб для построения полностью настраиваемых сред с несколькими контейнерами, которые могут использовать общие сети и тома хранения данных.

      В этом обучающем модуле мы покажем, как установить Docker Compose на сервере под управлением Ubuntu 20.04 и начать использовать этот инструмент.

      Предварительные требования

      Для его выполнения вам потребуется следующее:

      Шаг 1 — Установка Docker Compose

      Чтобы получить самую последнюю стабильную версию Docker Compose, мы загрузим это программное обеспечение из официального репозитория Github.

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

      Следующая команда загружает версию 1.26.0 и сохраняет исполняемый файл в каталоге /usr/local/bin/docker-compose, в результате чего данное программное обеспечение будет глобально доступно под именем docker-compose:

      • sudo curl -L "https://github.com/docker/compose/releases/download/1.26.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

      Затем необходимо задать правильные разрешения, чтобы сделать команду docker-compose исполняемой:

      • sudo chmod +x /usr/local/bin/docker-compose

      Чтобы проверить успешность установки, запустите следующую команду:

      Вывод будет выглядеть следующим образом:

      Output

      docker-compose version 1.26.0, build 8a1c60f6

      Установка Docker Compose успешно выполнена. В следующем разделе мы покажем, как настроить файл docker-compose.yml и запустить контейнерную среду с помощью этого инструмента.

      Шаг 2 — Настройка файла docker-compose.yml

      Чтобы продемонстрировать настройку файла docker-compose.yml и его работу с Docker Compose, мы создадим среду веб-сервера, используя официальный образ Nginx из Docker Hub, публичного реестра Docker. Контейнерная среда будет обслуживать один статичный файл HTML.

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

      • mkdir ~/compose-demo
      • cd ~/compose-demo

      Настройте в этом каталоге папку приложения, которая будет выступать в качестве корневого каталога документов для вашей среды Nginx:

      Создайте в предпочитаемом текстовом редакторе новый файл index.html в папке app:

      Вставьте в файл следующее содержимое:

      ~/compose-demo/app/index.html

      <!doctype html>
      <html lang="en">
      <head>
          <meta charset="utf-8">
          <title>Docker Compose Demo</title>
          <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/kognise/water.css@latest/dist/dark.min.css">
      </head>
      <body>
      
          <h1>This is a Docker Compose Demo Page.</h1>
          <p>This content is being served by an Nginx container.</p>
      
      </body>
      </html>
      

      Сохраните и закройте файл после завершения. Если вы использовали nano, нажмите CTRL+X, а затем Y и ENTER для подтверждения.

      Затем создайте файл docker-compose.yml:

      Вставьте в файл docker-compose.yml следующее содержимое:

      docker-compose.yml

      version: '3.7'
      services:
        web:
          image: nginx:alpine
          ports:
            - "8000:80"
          volumes:
            - ./app:/usr/share/nginx/html
      

      Файл docker-compose.yml обычно начинается с определения версии. Оно показывает Docker Compose, какую версию конфигурации мы используем.

      Далее идет блок services, где настраиваются службы, являющиеся частью этой среды. В нашем примере у нас имеется одна служба с именем web. Эта служба использует образ nginx:alpine и настраивает переадресацию портов с помощью директивы ports. Все запросы порта 8000 на компьютере host (система, где вы запускаете Docker Compose) будут перенаправляться в контейнер web на порту 80, где будет работать Nginx.

      Директива volumes создаст общий том для хоста и контейнера. Контейнер будет предоставлен доступ к локальной папке app, а том будет располагаться в каталоге /usr/share/nginx/html внутри контейнера, который заменит корневой каталог документов Nginx по умолчанию.

      Сохраните и закройте файл.

      Мы настроили демонстрационную страницу и файл docker-compose.yml для создания контейнерной среды веб-сервера, которая будет обслуживать ее. На следующем шаге мы запустим эту среду с помощью Docker Compose.

      Шаг 3 — Запуск Docker Compose

      Теперь у нас имеется файл docker-compose.yml, и мы можем использовать Docker Compose для запуска нашей среды. Следующая команда загрузит необходимые образы Docker, создаст контейнер для службы web и запустит контейнерную среду в фоновом режиме:

      Docker Compose будет вначале искать заданный образ в локальной системе, и если не найдет его, загрузит его из Docker Hub. Вывод будет выглядеть следующим образом:

      Output

      Creating network "compose-demo_default" with the default driver Pulling web (nginx:alpine)... alpine: Pulling from library/nginx cbdbe7a5bc2a: Pull complete 10c113fb0c77: Pull complete 9ba64393807b: Pull complete c829a9c40ab2: Pull complete 61d685417b2f: Pull complete Digest: sha256:57254039c6313fe8c53f1acbf15657ec9616a813397b74b063e32443427c5502 Status: Downloaded newer image for nginx:alpine Creating compose-demo_web_1 ... done

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

      Эта команда покажет вам информацию о работающих контейнерах и их состоянии, а также о действующей переадресации портов:

      Output

      Name Command State Ports ---------------------------------------------------------------------------------- compose-demo_web_1 /docker-entrypoint.sh ngin ... Up 0.0.0.0:8000->80/tcp

      Для получения доступа к демонстрационному приложению введите в браузере адрес localhost:8000, если оно запущено на локальном компьютере, или your_server_domain_or_IP:8000, если оно запущено на удаленном сервере.

      Страница будет выглядеть следующим образом:

      Страница демонстрационного приложения Docker Compose

      Заданный в файле docker-compose.yml общий том синхронизирует файлы в папке app с корневым каталогом документов контейнера. Если вы внесете любые изменения в файл index.html, они будут автоматически отражены в контейнере и появятся в браузере после перезагрузки страницы.

      На следующем шаге вы увидите, как управлять контейнерной средой с помощью команд Docker Compose.

      Шаг 4 — Знакомство с командами Docker Compose

      Мы рассмотрели процедуру настройки файла docker-compose.yml и запуск среды с помощью команды docker-compose up. Теперь мы посмотрим, как использовать команды Docker Compose для управления контейнерной средой и взаимодействия с ней.

      Чтобы посмотреть журналы контейнера Nginx, используйте команду logs:

      Вывод будет выглядеть следующим образом:

      Output

      Attaching to compose-demo_web_1 web_1 | /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration web_1 | /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/ web_1 | /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh web_1 | 10-listen-on-ipv6-by-default.sh: Getting the checksum of /etc/nginx/conf.d/default.conf web_1 | 10-listen-on-ipv6-by-default.sh: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf web_1 | /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh web_1 | /docker-entrypoint.sh: Configuration complete; ready for start up web_1 | 172.22.0.1 - - [02/Jun/2020:10:47:13 +0000] "GET / HTTP/1.1" 200 353 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36" "-"

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

      Output

      Pausing compose-demo_web_1 ... done

      Чтобы возобновить работу после приостановки, используйте команду:

      Output

      Unpausing compose-demo_web_1 ... done

      Команда stop останавливает выполнение контейнера, но не уничтожает данные, связанные с вашими контейнерами:

      Output

      Stopping compose-demo_web_1 ... done

      Если вы хотите удалить контейнеры, сети и тома, связанные с контейнерной средой, используйте команду down:

      Output

      Removing compose-demo_web_1 ... done Removing network compose-demo_default

      Обратите внимание, что при этом не будет удален базовый образ, используемый Docker Compose для запуска нашей среды (в нашем примере nginx:alpine). Так, при повторном запуске среды с помощью команды docker-compose up процесс будет намного быстрее, поскольку образ уже находится в вашей системе.

      Если вы хотите удалить из системы базовый образ, используйте команду:

      • docker image rm nginx:alpine

      Output

      Untagged: nginx:alpine Untagged: nginx@sha256:b89a6ccbda39576ad23fd079978c967cecc6b170db6e7ff8a769bf2259a71912 Deleted: sha256:7d0cdcc60a96a5124763fddf5d534d058ad7d0d8d4c3b8be2aefedf4267d0270 Deleted: sha256:05a0eaca15d731e0029a7604ef54f0dda3b736d4e987e6ac87b91ac7aac03ab1 Deleted: sha256:c6bbc4bdac396583641cb44cd35126b2c195be8fe1ac5e6c577c14752bbe9157 Deleted: sha256:35789b1e1a362b0da8392ca7d5759ef08b9a6b7141cc1521570f984dc7905eb6 Deleted: sha256:a3efaa65ec344c882fe5d543a392a54c4ceacd1efd91662d06964211b1be4c08 Deleted: sha256:3e207b409db364b595ba862cdc12be96dcdad8e36c59a03b7b3b61c946a5741a

      Примечание. Чтобы получить более подробное описание команд Docker, ознакомьтесь с нашим руководством по установке и использованию Docker.

      Заключение

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

      Полный справочник всех доступных команд docker-compose можно найти в официальной документации.



      Source link

      Использование модуля collections в Python 3


      Автор выбрал COVID-19 Relief Fund для получения пожертвования в рамках программы Write for DOnations.

      Введение

      В Python 3 имеется множество встроенных структур данных, включая кортежи, словари и списки. Структуры данных дают нам возможности организации и хранения данных. Модуль collections помогает эффективно заполнять структуры данных и управлять ими.

      В этом обучающем модуле мы рассмотрим три класса модуля collections, которые помогут вам работать с кортежами, словарями и списками. Мы используем namedtuples для создания кортежей с именованными полями, defaultdict для удобного группирования информации в словарях и деки для эффективного добавления элементов на любую сторону объекта в виде списка.

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

      Предварительные требования

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

      Добавление именованных полей в кортежи

      Кортежи Python представляют собой неизменяемые упорядоченные последовательности элементов. Кортежи часто используются для представления табличных данных, например, строк из файла CSV или базы данных SQL. Аквариум может отслеживать список рыбок как серию кортежей.

      Кортеж отдельной рыбки:

      ("Sammy", "shark", "tank-a")
      

      Этот кортеж состоит из трех строковых элементов.

      Хотя это полезно, данный массив четко не показывает, что означает каждое из полей. На самом деле элемент 0 — это имя, элемент 1 — это вид, а элемент 2 — резервуар.

      Значение полей кортежа fish:

      name species tank
      Sammy shark tank-a

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

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

      Давайте используем namedtuple для генерирования класса, который дает четкие имена каждому элементу кортежа fish:

      from collections import namedtuple
      
      Fish = namedtuple("Fish", ["name", "species", "tank"])
      

      from collections import namedtuple дает вашей программе Python доступ к функции фабрики namedtuple. Вызов функции namedtuple() возвращает класс, привязанный к имени Fish. Функция namedtuple() имеет два аргумента: желаемое имя нового класса "Fish" и список именованных элементов ["name", "species", "tank"].

      Мы можем использовать класс Fish для представления описанного выше кортежа fish:

      sammy = Fish("Sammy", "shark", "tank-a")
      
      print(sammy)
      

      Если мы выполним этот код, мы увидим следующие результаты:

      Output

      Fish(name="Sammy", species="shark", tank='tank-a')

      Создается экземпляр sammy с использованием класса Fish. sammy — это кортеж из трех элементов с понятными именами.

      Доступ к полям sammy осуществляется по имени или через традиционный индекс кортежа:

      print(sammy.species)
      print(sammy[1])
      

      Если мы выполним эти два вызова print, результат будет выглядеть так:

      Output

      shark shark

      При доступе к.species возвращается то же значение, что и при доступе ко второму элементу sammy с использованием [1].

      Использование namedtuple из модуля collections делает программу более удобочитаемой и при этом позволяет сохранить важные свойства кортежа (неизменяемость и упорядоченность).

      Кроме того, функция фабрики namedtuple добавляет несколько дополнительных методов в экземпляры Fish.

      Используйте ._asdict() для конвертации экземпляра в словарь:

      print(sammy._asdict())
      

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

      Output

      {'name': 'Sammy', 'species': 'shark', 'tank': 'tank-a'}

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

      В версиях Python ниже 3.8 эта строка может выводиться немного по-другому. Например, вместо показанного здесь простого словаря вы можете увидеть OrderedDict.

      Примечание. В Python методы, начинающиеся с символа подчеркивания, обычно считаются «частными». При этом дополнительные методы, предоставляемые namedtuple (_asdict(), ._make(), ._replace() и т. д.) являются публичными.

      Сбор данных в словаре

      Часто бывает полезно собирать данные в словарях Python. Словарь defaultdict из модуля collections может помочь быстро и кратко собрать информацию в словарях.

      defaultdict никогда не генерирует ошибку KeyError. При отсутствии ключа defaultdict просто вставляет и возвращает подстановочное значение:

      from collections import defaultdict
      
      my_defaultdict = defaultdict(list)
      
      print(my_defaultdict["missing"])
      

      Если запустить этот код, результат будет выглядеть следующим образом:

      Output

      []

      defaultdict вставляет и возвращает подстановочное значение вместо генерирования ошибки KeyError. В этом случае мы задали подстановочное значение как список.

      Обычные словари в подобных случаях выдают ошибку KeyError при отсутствии ключей:

      my_regular_dict = {}
      
      my_regular_dict["missing"]
      

      Если запустить этот код, результат будет выглядеть следующим образом:

      Output

      Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 'missing'

      Обычный словарь my_regular_dict генерирует ошибку KeyError при попытке доступа к отсутствующему ключу.

      Поведение defaultdict отличается от поведения обычного словаря. Вместо генерирования ошибки KeyError при отсутствии ключа defaultdict вызывает подстановочное значение без аргументов для создания нового объекта. В данном случае это list() для создания пустого списка.

      Продолжим рассмотрение нашего примера вымышленного аквариума. Допустим, у нас имеется список кортежей fish, представляющий содержимое аквариума:

      fish_inventory = [
          ("Sammy", "shark", "tank-a"),
          ("Jamie", "cuttlefish", "tank-b"),
          ("Mary", "squid", "tank-a"),
      ]
      

      В аквариуме три рыбки, и их названия, виды и резервуары указываются в этих трех кортежах.

      Наша цель — организовать инвентарный список по резервуарам и получить список рыб, присутствующих в каждом резервуаре. Другими словами, нам нужен словарь, сопоставляющий "tank-a" с ["Jamie", "Mary"] и "tank-b" с ["Jamie"].

      Мы можем использовать defaultdict для группировки рыб по резервуарам:

      from collections import defaultdict
      
      fish_inventory = [
          ("Sammy", "shark", "tank-a"),
          ("Jamie", "cuttlefish", "tank-b"),
          ("Mary", "squid", "tank-a"),
      ]
      fish_names_by_tank = defaultdict(list)
      for name, species, tank in fish_inventory:
          fish_names_by_tank[tank].append(name)
      
      print(fish_names_by_tank)
      

      Запустив этот код, мы получим следующий результат:

      Output

      defaultdict(<class 'list'>, {'tank-a': ['Sammy', 'Mary'], 'tank-b': ['Jamie']})

      fish_names_by_tank декларируется как словарь defaultdict, который по умолчанию вставляет list() вместо вывода ошибки KeyError. Поскольку при этом каждый ключ в fish_names_by_tank будет гарантированно указывать на список, мы можем свободно вызывать .append() для добавления имен в списки каждого резервуара.

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

      Без defaultdict цикл for выглядел бы примерно так:

      More Verbose Example Without defaultdict

      ...
      
      fish_names_by_tank = {}
      for name, species, tank in fish_inventory:
          if tank not in fish_names_by_tank:
            fish_names_by_tank[tank] = []
          fish_names_by_tank[tank].append(name)
      

      Если используется обычный словарь (вместо defaultdict), цикл for всегда должен проверять наличие определенного резервуара в fish_names_by_tank. Имя рыбки можно добавить только после подтверждения наличия резервуара в fish_names_by_tank или его инициализации с помощью [].

      defaultdict помогает сократить объем кода при заполнении словарей, поскольку никогда не генерирует ошибки KeyError.

      Использование дек для эффективного добавления элементов с любой стороны набора

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

      С точки зрения нотации Big O добавление в список представляет собой операцию O(1) с постоянным временем. Вставка в начало списка выполняется медленнее, с производительностью O(n).

      Примечание. Инженеры-разработчики часто измеряют производительность процедур с помощью нотации «Big O». Если размер ввода не влияет на время выполнения процедуры, считается, что она выполняется за постоянное время или O(1) (“Big O of 1”). Как уже говорилось выше, Python может добавлять элементы в списки за постоянное время или O(1).

      Иногда размер ввода напрямую влияет на время выполнения процедуры. Например, вставка в начало списка Python выполняется тем медленнее, чем больше элементов содержится в списке. В нотации Big O для определения размера ввода используется буква n. Это означает, что для добавления элементов в начало списка Python требуется «линейное время» или O(n) (“Big O of n”).

      В целом процедуры O(1) быстрее процедур O(n).

      Мы можем выполнить вставку в начало списка Python:

      favorite_fish_list = ["Sammy", "Jamie", "Mary"]
      
      # O(n) performance
      favorite_fish_list.insert(0, "Alice")
      
      print(favorite_fish_list)
      

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

      Output

      ['Alice', 'Sammy', 'Jamie', 'Mary']

      Метод .insert(index, object) в списке позволяет нам вставить "Alice" в начало списка favorite_fish_list. Следует отметить, что вставка в начало списка осуществляется с производительностью O(n). По мере увеличения длины списка favorite_fish_list увеличивается и время вставки имени рыбки в начало списка.

      дека (двусторонняя очередь) из модуля collections — это объект списка, позволяющий вставлять элементы в начало или конец последовательности за постоянное время (O(1)).

      Вставьте элемент в начало деки:

      from collections import deque
      
      favorite_fish_deque = deque(["Sammy", "Jamie", "Mary"])
      
      # O(1) performance
      favorite_fish_deque.appendleft("Alice")
      
      print(favorite_fish_deque)
      

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

      Output

      deque(['Alice', 'Sammy', 'Jamie', 'Mary'])

      Мы можем создать экземпляр деки, используя готовый набор элементов. В данном случае это список из трех имен любимых рыбок. Вызов метода favorite_fish_deque appendleft позволяет нам вставить элемент в начало набора с производительностью O(1). Производительность O(1) означает, что время добавления элемента в начало favorite_fish_deque не увеличивается, даже если favorite_fish_deque содержит тысячи или миллионы элементов.

      Примечание. Хотя дека позволяет добавлять записи в начало последовательности более эффективно, чем список, дека не выполняет все операции более эффективно, чем список. Например, доступ к случайному элементу деки осуществляется с производительностью O(n), а доступ к случайному элементу списка — с производительностью O(1). Деки используются, когда нам важно быстро вставлять и удалять элементы с любой стороны набора. Полное сравнение производительности по времени можно найти в вики-справочнике по Python.

      Заключение

      Модуль collections — мощный элемент стандартной библиотеки Python, делающий работу с данными более краткой и эффективной. В этом обучающем модуле мы рассказали о трех классах модуля collections: namedtuple, defaultdict и deque.

      Теперь вы можете использовать документацию по модулю collection, чтобы узнать больше о других доступных классах и утилитах. Чтобы узнать больше о Python в целом, пройдите нашу серию обучающих модулей «Программирование на Python 3».



      Source link