Задание последовательности в python

/привет/мир/etc

Ниже обзор возможностей и приемов работы с последовательностями в Python 3, включая следующие темы (но не ограничиваясь ими):

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

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

[1, 13, 42, -7, 5] # список (1, 2, 3, 5) # кортеж 'привет' # строка b'hello' # строка байтов bytearray([100, 101, 102]) # массив байтов 

Список ( list ) и массив байтов ( bytearray ) — изменяемые последовательности, кортеж ( tuple ), строка ( str ) и строка байтов ( bytes ) — неизменяемые.

Примеры последовательностей, построенных с помощью итератора, предоставленного функцией range() :

>>> list(range(10)) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> tuple(range(10)) (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) >>> bytes(range(10)) b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t' >>> str(bytes(range(65, 91)), encoding='ascii') 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 

Функция range() также позволяет задать шаг, в том числе отрицательный:

>>> list(range(1, 10, 2)) [1, 3, 5, 7, 9] >>> list(range(10, 1, -2)) [10, 8, 6, 4, 2] 

Генератор — это объект класса generator , который Python автоматически создает при вызове функции c предложением yield внутри:

>>> def down(n): . while n > 0: . yield n . n -= 1 . >>> type(down) >>> down10 = down(10) >>> type(down10)

Каждый генератор — это итератор, так что можно инициализировать последовательность с его помощью:

>>> list(down10) [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] >>> bytes(down(127)) b'\x7f~>|= 

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

  • индексирование от начала последовательности начинается с 0 и заканчивается числом, равным длине последовательности минус один, а
  • индексирование с конца начинается с -1 и заканчивается отрицательным числом, равным длине последовательности.
>>> a = [1, 5, 13] >>> a[0] 1 >>> a[1] = 3 >>> a [1, 3, 13] >>> a[2] 13 >>> a[-1] 13 >>> a[-2] = 5 >>> a [1, 5, 13] >>> a[-3] 1 

Замечательной возможностью является срез ( slice ), позволяющий из исходной последовательности получить подпоследовательность, задаваемую начальным и конечным индексами и (опционально) шагом. В сущности, срез - это фильтр по индексу, порождающий из исходной последовательности новую:

>>> a [1, 5, 13] >>> a[1:2] # c 1-го по 2-й элемент, не включая последний [5] >>> a[1:] # c 1-го до последнего элемента [5, 13] >>> a[:2] # c нулевого по 2-й элемент, не включая последний [1, 5] >>> a[::-1] # отрицательный шаг работает от конца к началу [13, 5, 1] 

В последнем примере последовательность обращается. Обращение последовательности нагляднее всего продемонстрировать на строке:

>>> 'привет python'[::-1] 'nohtyp тевирп' 

Срез без ограничений с двух сторон включает все элементы исходной последовательности и создает ее копию:

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

>>> a is a[:] False >>> s = 'привет' >>> s is s[:] True >>> b = b'hello' >>> b is b[:] True 

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

>>> a [1, 5, 13] >>> a[:1] = [5, 6] >>> a [5, 6, 5, 13] >>> a[:] = [-1] >>> a [-1] 

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

>>> a[:] = [1, 2, 3, 4] >>> a[::2] [1, 3] >>> a[::2] = [-1, -1] >>> a [-1, 2, -1, 4] >>> a[::-1] = range(4) >>> a [3, 2, 1, 0] >>> a[::-1] = -1 Traceback (most recent call last): File "", line 1, in TypeError: must assign iterable to extended slice 

Следующая таблица представляет операции и методы, общие для всех последовательностей Python:

Операция Описание Работает с range ?
x in s True, если в s есть элемент, равный x, иначе False да
x not in s False, если в s нет элемента, равного x, иначе True да
s + t конкатенация s и t
s * n или n * s конкатенация s с собой n раз
s[i] i-ый элемент s, считая с 0 да
s[i:j] срез s от i до j да
s[i:j:k] срез s от i до j с шагом k да
len(s) длина s да
min(s) наименьший элемент s да
max(s) наибольший элемент s да
s.index(x[, i[, j]]) индекс первого вхождения x в s (начиная с i и заканчивая j) да
s.count(x) всего вхождений x в s да

Операции in и not in для строк и строк байтов способны проверить вхождение не только отдельных элементов, но и подпоследовательностей из нескольких элементов:

>>> 'o' in 'hello' True >>> 'hell' in 'hello' True >>> 'bye' not in 'hello' True >>> b'o' in b'hello' True >>> b'hell' in b'hello' True >>> b'bye' in b'hello' False 

Для других последовательностей проверятся вхождение ровно одного элемента:

>>> a [3, 2, 1, 0] >>> [3, 2] in a False >>> 3 in a True >>> 2 in a True >>> [3, 2] in [[3, 2], [1, 0]] True 

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

>>> 'hello' + ' python!' 'hello python!' >>> [-1, 0] + [1, True, None] [-1, 0, 1, True, None] >>> [-1, 0] * 5 [-1, 0, -1, 0, -1, 0, -1, 0, -1, 0] >>> 5 * [-1, 0] [-1, 0, -1, 0, -1, 0, -1, 0, -1, 0] 

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

>>> range(10)[5] 5 >>> range(10)[5:] range(5, 10) >>> range(10)[::-1] range(9, -1, -1) >>> 7 in range(10) True >>> 10 in range(10) False >>> max(range(10)) 9 >>> min(range(10)) 0 >>> len(range(10)) 10 >>> range(10).count(5) 1 >>> range(10).index(5) 5 

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

>>> a [3, 2, 1, 0] >>> filter(lambda x: x > 2, a) >>> list(filter(lambda x: x > 1, a)) [3, 2] 

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

>>> map(lambda x: x**2, a) >>> list(map(lambda x: x**2, a)) [9, 4, 1, 0] 

Композиция filter() и map() позволяет и отфильтровать элементы по значению и получить новые значения из исходных:

>>> a [3, 2, 1, 0] >>> list(map(lambda x: x**2, filter(lambda x: x > 2, a))) [9] >>> list(filter(lambda x: x > 2, map(lambda x: x**2, a))) [9, 4] 

Конструкция list comprehension (как это по-русски?) может работать как filter() , map() или их комбинация:

>>> # filter >>> [x for x in a if x > 2] [3] >>> # map >>> [x**2 for x in a] [9, 4, 1, 0] >>> # filter then map >>> [x**2 for x in a if x > 2] [9] >>> # map then filter >>> [x for x in [x**2 for x in a] if x > 2] [9, 4] 

А следующий фрагмент демонстрирует, как с помощью list comprehension и метода count() найти повторяющиеся элементы в последовательности:

>>> s = 'qwertyq' >>> [x for x in s if s.count(x) > 1] ['q', 'q'] 

List comprehension поддерживает вложенность как циклов, так и условий:

>>> b = [ . [0, 1, 2], . [3, 4, 5] . ] >>> [y for x in b for y in x] [0, 1, 2, 3, 4, 5] 

Последнее предложение эквивалентно следующему фрагменту:

>>> a = [] >>> for x in b: . for y in x: . a.append(y) . >>> a [0, 1, 2, 3, 4, 5] 

Вложенные условия в list comprehension эквивалентны составному условию с оператором and или вложенным if внутри цикла:

>>> [x for x in a if x > 0 if x < 5] [1, 2, 3, 4] >>> [x for x in a if x > 0 and x < 5] [1, 2, 3, 4] >>> c = [] >>> for x in a: . if x > 0: . if x < 5: . c.append(x) . >>> c [1, 2, 3, 4] 

Если list comprehension немедленно порождает список (объект класса list ), то генераторное выражение, заключенное, в отличие от list comprehension, в обычные скобки ( и ) , порождает генератор, который будет возвращать элементы последовательности, когда они понадобятся:

>>> a [0, 1, 2, 3, 4, 5] >>> g = (x*x for x in a if x%2 == 0) >>> type(g) >>> list(g) [0, 4, 16] 

От list comprehension генераторное выражение отличается только ленивым предоставлением элементов последовательности, в остальном поддерживая синтаксис и семантику list comprehension. Для передачи генераторного выражения в качестве аргумента функции достаточно одной пары скобок:

>>> max(x*x for x in a if x%2 == 0) 16 >>> bytes(x*x for x in a if x%2 == 0) b'\x00\x04\x10' >>> list(x*x for x in a if x%2 == 0) [0, 4, 16] 

Встроенные функции max() , min() и sum() возвращают максимальное, минимальное значение и сумму элементов последовательности, соответственно. Вместо готовой последовательности эти функции принимают также итерируемые объекты:

>>> a [0, 1, 2, 3, 4, 5] >>> max(a) 5 >>> min(a) 0 >>> min('qwerty') 'e' >>> max('qwerty') 'y' >>> sum(a) 15 >>> sum(x*x for x in a if x%2 == 0) 20 

Что если нужно найти не сумму, а произведение элементов? Или сумму их квадратов? С этим нам поможет функция reduce() из модуля functools :

>>> a = a[1:] >>> a [1, 2, 3, 4, 5] >>> from functools import reduce >>> reduce(lambda x, y: x*y, a) 120 

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

>>> reduce(lambda x, y: x+y*2, 'qwerty', '') 'qqwweerrttyy' 

Мы удвоили каждую букву в слове, но без третьего аргумента этого бы сделать не удалось:

>>> reduce(lambda x, y: x+y*2, 'qwerty') 'qwweerrttyy' 

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

>>> reduce(lambda x, y: x+y**2, a, 0) 55 

Еще две встроенные функции, сводящие последовательность к единственному значению, - это any() и all() , возвращающие булевы значения.

>>> a [1, 2, 3, 4, 5] >>> any(a) True >>> all(a) True >>> any(x-1 for x in a) True >>> all(x-1 for x in a) False 

Функция any() возвращает True , если хотя бы один из элементов последовательности оценивается как True , иначе - возвращает False . Функция all() возвращает True , если все элементы последовательности оцениваются как True , иначе - False . С помощью этих функций и генераторного выражения легко проверить, удовлетворяют ли элементы последовательности некоторому условию:

>>> any(x>5 for x in a) False >>> all(x 

Циклы, list comprehension и генераторное выражение позволяют обходить все элементы последовательности. А для параллельного обхода нескольких последовательностей - двух и более - Python предлагает функцию zip() :

>>> a [1, 2, 3, 4, 5] >>> b = 'hello' >>> c = range(-5, 0) >>> for x, y, z in zip(a, b, c): . print(x, y, z) . 1 h -5 2 e -4 3 l -3 4 l -2 5 o -1 

Функция zip() завершает работу по концу самой короткой из последовательностей:

>>> b = 'bye' >>> for x, y, z in zip(a, b, c): . print(x, y, z) . 1 b -5 2 y -4 3 e -3 

Если необходимо дойти до конца самой длинной из последовательностей, то нужно воспользоваться функцией zip_longest() из модуля itertools :

>>> from itertools import zip_longest >>> for x, y, z in zip_longest(a, b, c): . print(x, y, z) . 1 b -5 2 y -4 3 e -3 4 None -2 5 None -1 

Вместо None на месте отсутствующих элементов можно получить значение, заданное с помощью именованного параметра fillvalue :

>>> for x, y, z in zip_longest(a, b, c, fillvalue='*'): . print(x, y, z) . 1 b -5 2 y -4 3 e -3 4 * -2 5 * -1 

В завершение обзора, приведу операции и методы, общие для изменяемых последовательностей, то есть, для list и bytearray :

Операция Описание
s[i] = x замена i-го элемента s на x
del s[i] удаление i-го элемента из s
s[i:j] = t замена среза s от i до j на содержимое t
del s[i:j] то же, что и s[i:j] = []
s[i:j:k] = t замена элементов s[i:j:k] на элементы t
del s[i:j:k] удаление элементов s[i:j:k] из s
s.append(x) добавление x в конец последовательности (эквивалентно s[len(s):len(s)] = [x] )
s.clear() удаление всех элементов из s (эквивалентно del s[:] )
s.copy() создание поверхностной копии s (эквивалентно s[:] )
s.extend(t)
или
s += t
расширяет s содержимым t
s *= n обновляет s его содержимым, повторенным n раз
s.insert(i, x) вставляет x в s по индексу i (эквивалентно s[i:i] = [x] )
s.pop([i]) извлекает элемент с индексом i и удаляет его из s
s.remove(x) удаляет первое вхождение x в s
s.reverse() меняет порядок элементов в s на обратный

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

Это был (неисчерпывающий) обзор возможностей и приемов работы с последовательностями в Python 3.

Источник

Читайте также:  Рандом java math random
Оцените статью