Главная страница
Навигация по странице:

  • Рис. 3.2.

  • Синтаксис Описание Функция sorted() , стр. 164, 170 138

  • Генераторы списков

  • Программирование на Python 3. Руководство издательство СимволПлюс


    Скачать 3.74 Mb.
    НазваниеРуководство издательство СимволПлюс
    Дата10.11.2022
    Размер3.74 Mb.
    Формат файлаpdf
    Имя файлаПрограммирование на Python 3.pdf
    ТипРуководство
    #780382
    страница17 из 74
    1   ...   13   14   15   16   17   18   19   20   ...   74
    Синтаксис
    Описание
    L.append (x)
    Добавляет элемент x в конец списка L
    L.count(x)
    Возвращает число вхождений элемента x в список L
    L.extend(m)
    L += m
    Добавляет в конец списка L все элементы итерируемого объ
    екта m; оператор += делает то же самое
    L.index(x,
    start,
    end)
    Возвращает индекс самого первого (слева) вхождения элемен
    та x в список L (или в срез start:end списка L), в противном слу
    чае возбуждает исключение ValueError
    L.insert(i, x)
    Вставляет элемент x в список L в позицию int i
    L.pop()
    Удаляет самый последний элемент из списка L и возвращает его в качестве результата
    L.pop(i)
    Удаляет из списка L элемент с индексом int i и возвращает его в качестве результата
    L[6]
    L[5]
    L[4]
    L[3]
    L[2]
    L[1]
    17.5
    'kilo'
    49
    'V'
    ['ram', 5, 'echo']
    7
    L[0]
    L[1]
    L[2]
    L[3]
    L[4]
    L[5]
    Рис. 3.2. Позиции элементов в списке

    Последовательности
    137
    Несмотря на то, что для доступа к элементам списка можно использо
    вать оператор извлечения среза, тем не менее в некоторых ситуациях бывает необходимо одновременно извлечь две или более частей списка.
    Сделать это можно с помощью операции распаковывания последова
    тельности. Любой итерируемый объект (списки, кортежи и другие) мо
    жет быть распакован с помощью оператора распаковывания «звездоч
    ка» (*). Когда слева от оператора присваивания указывается две или более переменных, одна из которых предваряется символом *, каждой переменной присваивается по одному элементу списка, а переменной со звездочкой присваивается оставшаяся часть списка. Ниже приво
    дится несколько примеров выполнения распаковывания списков:
    >>> first, *rest = [9, 2, 4, 8, 7]
    >>> first, rest
    (9, [2, 4, 8, 7])
    >>> first, *mid, last = "Charles Philip Arthur George Windsor".split()
    >>> first, mid, last
    ('Charles', ['Philip', 'Arthur', 'George'], 'Windsor')
    >>> *directories, executable = "/usr/local/bin/gvim".split("/")
    >>> directories, executable
    (['', 'usr', 'local', 'bin'], 'gvim')
    Когда используется оператор распаковывания последовательности,
    как в данном примере, выражение *rest и подобные ему называются
    выражениями со звездочкой
    В языке Python имеется также похожее понятие аргументов со звез
    дочкой
    . Например, допустим, что имеется следующая функция, при
    нимающая три аргумента:
    def product(a, b, c):
    return a * b * c # здесь * – это оператор умножения тогда мы можем вызывать эту функцию с тремя аргументами или ис
    пользовать аргументы со звездочкой:
    >>> product(2, 3, 5)
    30
    >>> L = [2, 3, 5]
    >>> product(*L)
    L.remove(x)
    Удаляет самый первый (слева) найденный элемент x из спи
    ска L или возбуждает исключение ValueError, если элемент x не будет найден
    L.reverse()
    Переставляет в памяти элементы списка в обратном порядке
    L.sort(...)
    Сортирует список в памяти. Этот метод при
    нимает те же необязательные аргументы key
    и reverse, что и встроенная функция sorted()
    Синтаксис
    Описание
    Функция
    sorted()
    , стр. 164, 170

    138
    Глава 3. Типы коллекций
    30
    >>> product(2, *L[1:])
    30
    В первом примере функция вызывается, как обычно, с тремя аргумен
    тами. Во втором вызове использован аргумент со звездочкой; в этом случае список из трех элементов распаковывается оператором *, так что функция получает столько аргументов, сколько ей требуется. Того же эффекта можно было бы добиться при использовании кортежа с тремя элементами. В третьем вызове функции первый аргумент пере
    дается традиционным способом, а другие два – посредством примене
    ния операции распаковывания двухэлементного среза списка L. Функ
    ции и передача аргументов полностью будут описываться в главе 4.
    В программах всегда однозначно известно, является оператор * опера
    тором умножения или оператором распаковывания последовательно
    сти. Когда он появляется слева от оператора присваивания – это опера
    тор распаковывания; когда он появляется гдето в другом месте (на
    пример, в вызове функции) – это оператор распаковывания, если он используется в одноместном операторе, и оператор умножения, если он используется в двухместном операторе.
    Мы уже знаем, что имеется возможность выполнять итерации по эле
    ментам списка с помощью конструкции for item in L:. Если в цикле потребуется изменять элементы списка, то можно использовать сле
    дующий прием:
    for i in range(len(L)):
    L[i] = process(L[i])
    Встроенная функция range() возвращает целочисленный итератор. С одним целочисленным аргументом, n, итера
    тор range() возвращает последовательность чисел 0, 1, …,
    n
    – 1.
    Этот прием можно использовать для увеличения всех элементов в спи
    ске целых чисел. Например:
    for i in range(len(numbers)):
    numbers[i] += 1
    Поскольку списки поддерживают возможность извлечения срезов,
    в определенных случаях один и тот же эффект может быть достигнут как с помощью оператора извлечения среза, так и с помощью одного из методов списков. Например, предположим, что имеется список woods = ["Cedar", "Yew", "Fir"]
    ; дополнить такой список можно двумя способами:
    woods += ["Kauri", "Larch"] woods.extend(["Kauri", "Larch"])
    В обоих случаях в результате будет получен список ['Cedar', 'Yew',
    'Fir', 'Kauri', 'Larch']
    Функция
    range()
    , стр. 167

    Последовательности
    139
    Удаление элементов с помощью инструкции del
    Несмотря на то, что название инструкции del вызывает ассоциа
    ции со словом delete (удалить), она не обязательно удаляет ка
    киелибо данные. Когда инструкция del применяется к элементу данных, который не является коллекцией, она разрывает связь между ссылкой на объект и самим элементом данных и удаляет
    ссылку на объект
    . Например:
    >>> x = 8143 # создается ссылка на объект 'x' и целое число 8143
    >>> x
    8143
    >>> del x # удаляется ссылка на объект 'x', число готово к утилизации
    >>> x
    Traceback (most recent call last):
    NameError: name 'x' is not defined
    (NameError: имя 'x' не определено)
    Когда удаляется ссылка на объект, если не осталось других ссы
    лок, указывающих на этот объект, то интерпретатор помечает элемент данных, на который указывала ссылка, как готовый к утилизации. Невозможно предсказать, когда произойдет ути
    лизация и произойдет ли она вообще (это зависит от реализации
    Python), поэтому, когда необходимо явно освободить память, де
    лать это придется вручную. Язык Python предоставляет два ре
    шения проблемы неопределенности. Одно из них состоит в ис
    пользовании конструкции try ... finally, которая гарантирует освобождение памяти, а другое заключается в использовании инструкции with, с которой мы познакомимся в главе 8.
    Когда инструкция del применяется к коллекциям, таким как кортежи или списки, удаляется только ссылка на эти коллек
    ции. Коллекция и ее элементы (а также элементы, которые сами являются коллекциями для своих элементов, рекурсивно) поме
    чаются как готовые к утилизации, если не осталось других ссы
    лок, указывающих на эти коллекции.
    Для изменяемых коллекций, таких как списки, инструкция del может применяться к отдельным элементам или срезам – в лю
    бом из этих случаев используется оператор извлечения среза [].
    Если для удаления предназначен элемент или элементы коллек
    ции и в программе не осталось ссылок, указывающих на эти эле
    менты, они помечаются, как готовые к утилизации.

    140
    Глава 3. Типы коллекций
    Отдельные элементы можно добавлять в конец списка с помощью ме
    тода list.append(). Элементы могут вставляться в любую позицию в списке с помощью метода list.insert() или посредством обращения к срезу с нулевой длиной. Например, допустим, что имеется список woods = ["Cedar", "Yew", "Fir", "Spruce"]
    ; тогда вставить новый эле
    мент в позицию с индексом 2 (то есть сделать этот элемент третьим эле
    ментом списка) можно одним из двух способов:
    woods[2:2] = ["Pine"] woods.insert(2, "Pine")
    В обоих случаях в результате будет получен список ['Cedar', 'Yew',
    'Pine', 'Fir', 'Spruce']
    Отдельные элементы списка можно изменять, выполняя присваива
    ние определенной позиции в списке, например, woods = 'Redwood'. Пу
    тем присваивания итерируемых объектов можно изменять целые срезы в списке, например, woods[1:3] = ["Spruce", "Sugi", "Rimu"]. Срез и ите
    рируемый объект не обязательно должны иметь одинаковую длину.
    В любом случае элементы, попавшие в срез, будут удалены, а на их ме
    сто будут вставлены элементы итерируемого объекта. Если длина ите
    рируемого объекта короче замещаемого среза, список уменьшится,
    а если длина итерируемого объекта больше замещаемого среза, то спи
    сок увеличится.
    Чтобы прояснить, что именно происходит в результате присваивания итерируемого объекта срезу списка, рассмотрим еще один пример.
    Представим, что имеется список L = ["A", "B", "C", "D", "E", "F"]
    и что выполняется присваивание итерируемого объекта (в данном слу
    чае – списка) срезу: L[2:5] = ["X", "Y"]. В первую очередь производит
    ся удаление элементов среза, то есть за кулисами список принимает вид ['A', 'B', 'F']. А затем все элементы итерируемого объекта встав
    ляются в позицию первого элемента среза, и в результате получается список ['A', 'B', 'X', 'Y', 'F'].
    Существует еще ряд других способов удаления элементов списка. Что
    бы удалить самый правый элемент списка, можно воспользоваться ме
    тодом list.pop() без аргументов – удаленный элемент возвращается в качестве результата. Аналогично можно использовать метод list.pop()
    с целочисленным значением индекса – для удаления (и возвращения)
    элемента с определенным индексом. Еще один способ удаления эле
    мента заключается в использовании метода list.remove(), которому передается удаляемый элемент. Также для удаления отдельных эле
    ментов или целых срезов можно использовать инструкцию del – на
    пример, del woods[4]. Кроме того, срезы могут удаляться путем при
    сваивания пустого списка, так следующие два фрагмента являются эк
    вивалентными:
    woods[2:4] = [] del woods[2:4]
    В левом фрагменте выполняется присваивание итерируемого объекта
    (пустого списка) срезу, то есть здесь сначала удаляются элементы сре

    Последовательности
    141
    за, а так как итерируемый объект, предназначенный для вставки,
    пуст, вставка не производится.
    Когда мы впервые обсуждали операцию извлечения раз
    реженного среза, мы делали это в контексте строк, где извлечение разреженных срезов выглядит не очень ин
    тересно. Но в случае списков операция извлечения раз
    реженного среза позволяет получить доступ к каждому
    n
    му элементу, что может оказаться очень удобно. На
    пример, предположим, что имеется список x = [1, 2, 3,
    4, 5, 6, 7, 8, 9, 10]
    и необходимо все элементы с нечет
    ными индексами (то есть x[1], x[3] и т. д.) установить в значение 0. Получить доступ к каждому второму эле
    менту можно, используя оператор среза с шагом, напри
    мер, x[::2]. Но такой оператор даст доступ к элементам с индексами 0, 2, 4 и т. д. Мы можем исправить положе
    ние, указав начальный индекс, использовав выражение x[1::2]
    , которое возвращает срез, содержащий нужные нам элементы. Чтобы установить все элементы среза в значение 0, нам необходим список нулей, и этот список должен содержать то же самое число нулей, сколько эле
    ментов содержится в срезе.
    Полное решение задачи выглядит так: x[1::2] = [0] * len(x[1::2]). Те
    перь список x имеет следующий вид [1, 0, 3, 0, 5, 0, 7, 0, 9, 0]. Мы вос
    пользовались оператором дублирования * для создания списка, содер
    жащего необходимое число нулей, основываясь на длине (то есть коли
    честве элементов) среза. Особенно интересно, что при присваивании списка [0, 0, 0, 0, 0] разреженному срезу, интерпретатор корректно за
    мещает первым нулем значение x[1], вторым нулем значение x[3] и т. д.
    Списки могут упорядочиваться в обратном порядке и сор
    тироваться, так же как и другие итерируемые объекты,
    с помощью встроенных функций reversed() и sorted(),
    описываемых в подразделе «Итераторы, функции и опе
    раторы для работы с итерируемыми объектами»
    (стр. 163). Списки также имеют эквивалентные методы list.reverse()
    и list.sort(), которые работают непосред
    ственно с самим списком (поэтому они ничего не возвра
    щают); последний из них принимает те же необязатель
    ные аргументы, что и функция sorted(). Для сортировки списков строк без учета регистра символов используется распространенный прием – например, список woods мож
    но было бы отсортировать так: woods.sort(key=str.lower).
    Аргумент key используется, чтобы определить функцию,
    которая будет применяться к каждому элементу, и воз
    вращать значение, участвующее в сравнении в процессе сортировки. Как уже отмечалось в предыдущей главе,
    Извлечение срезов из строк, стр. 89
    Функция
    sorted()
    , стр. 164, 170

    142
    Глава 3. Типы коллекций в разделе, где рассматривались вопросы сравнения строк (стр. 63), для языков, отличных от английского, сортировка строк в подразумевае
    мом людьми порядке может оказаться непростым делом.
    Что касается вставки элементов, списки обеспечивают лучшую произ
    водительность при добавлении или удалении элементов в конце списка
    (list.append(), list.pop()). Падение производительности происходит,
    когда приходится отыскивать элементы в списке, например, с помо
    щью методов list.remove() или list.index(), а также при использова
    нии оператора in проверки на вхождение. Когда необходимо обеспе
    чить высокую скорость поиска или проверки на вхождение, возмож
    но, более удачным выбором будут множества и словари (оба типа кол
    лекций описываются ниже, в этой же главе). Однако и для списков можно обеспечить высокую скорость поиска, если хранить их в отсор
    тированном виде – в языке Python алгоритм сортировки особенно хо
    рошо оптимизирован для случая сортировки частично отсортирован
    ных списков – и отыскивать элементы методом дихотомии (реализо
    ван в модуле bisect). (В главе 6 мы создадим свой класс списков, кото
    рые хранятся в отсортированном виде.)
    Генераторы списков
    Небольшие списки часто создаются как литералы, но длинные списки обычно создаются программным способом. Списки целых чисел могут создаваться с помощью выражения list(range(n)); когда необходим итератор целых чисел, достаточно функции range(); а для создания списков других типов часто используется оператор цикла for ... in.
    Предположим, например, что нам требуется получить список високос
    ных годов в определенном диапазоне. Для начала мы могли бы исполь
    зовать такой цикл:
    leaps = []
    for year in range(1900, 1940):
    if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0):
    leaps.append(year)
    Когда функции range() передаются два целочисленных аргумента n и m, итератор возвращает последователь
    ность целых чисел n, n + 1, …, m

    1.
    Конечно, если диапазон известен заранее, можно было бы использовать литерал списка, например, leaps = [1904,
    1908, 1912, 1916, 1920, 1924, 1928, 1932, 1936]
    Генератор списков
    – это выражение и цикл с дополнительным услови
    ем, заключенное в квадратные скобки, в котором цикл используется для создания элементов списка, а условие используется для исключе
    ния нежелательных элементов. В простейшем виде генератор списков записывается, как показано ниже:
    [item for item in iterable]
    Функция
    range()
    , стр. 167

    Последовательности
    143
    Это выражение вернет список всех элементов объекта iterable и семан
    тически ничем не отличается от выражения list(iterable). Интерес
    ными генераторы списков делают две особенности – они могут исполь
    зоваться как выражения и они допускают включение условной инст
    рукции, вследствие чего мы получаем две типичные синтаксические конструкции использования генераторов списков:
    [expression for item in iterable]
    [expression for item in iterable if condition]
    Вторая форма записи эквивалентна циклу:
    temp = []
    for item in iterable:
    if condition:
    temp.append(expression)
    Обычно выражение expression является либо самим элементом item,
    либо некоторым выражением с его участием. Конечно, генератору списков не требуется временная переменная temp[], которая необходи
    ма в версии с циклом for ... in.
    Теперь можно переписать программный код создания списка високос
    ных годов с использованием генератора списка. Мы сделаем это в три этапа. Сначала создадим список, содержащий все годы в указанном диапазоне:
    leaps = [y for y in range(1900, 1940)]
    То же самое можно было бы сделать с помощью выражения leaps =
    list(range(1900, 1940))
    . Теперь добавим простое условие, которое будет оставлять в списке только каждый четвертый год:
    leaps = [y for y in range(1900, 1940) if y % 4 == 0]
    И, наконец, получаем окончательную версию:
    leaps = [y for y in range(1900, 1940)
    if (y % 4 == 0 and y % 100 != 0) or (y % 400 == 0)]
    Использование генератора списков в данном случае позволило умень
    шить объем программного кода с четырех строк до двух – не так мно
    го, но в крупных проектах суммарная экономия может оказаться весь
    ма существенной.
    Так как генераторы списков воспроизводят списки, то есть итерируе
    мые объекты, и сами генераторы списков используют итерируемые объекты, имеется возможность вкладывать генераторы списков друг в друга. Это эквивалентно вложению циклов for ... in. Например, ес
    ли бы нам потребовалось сгенерировать список всех возможных кодов одежды для разных полов, разных размеров и расцветок, но исключая одежду для полных женщин, нужды и чаянья которых индустрия мо
    ды нередко игнорирует, мы могли бы использовать вложенные циклы for ... in
    , как показано ниже:

    1   ...   13   14   15   16   17   18   19   20   ...   74


    написать администратору сайта