Основы последовательностей в Python

PythonPythonIntermediate
Практиковаться сейчас

This tutorial is from open-source community. Access the source code

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

Последовательности Python - это упорядоченные коллекции элементов. Они индексируются целыми числами.

Это Guided Lab, который предоставляет пошаговые инструкции, чтобы помочь вам учиться и практиковаться. Внимательно следуйте инструкциям, чтобы выполнить каждый шаг и получить практический опыт. Исторические данные показывают, что это лабораторная работа уровня средний с процентом завершения 80%. Он получил 100% положительных отзывов от учащихся.

Типы данных последовательностей

В Python есть три последовательных типа данных.

  • Строка: 'Hello'. Строка - это последовательность символов.
  • Список: [1, 4, 5].
  • Кортеж: ('GOOG', 100, 490.1).

Все последовательности упорядочены, индексируются целыми числами и имеют длину.

a = 'Hello'               ## Строка
b = [1, 4, 5]             ## Список
c = ('GOOG', 100, 490.1)  ## Кортеж

## Индексированный порядок
a[0]                      ## 'H'
b[-1]                     ## 5
c[1]                      ## 100

## Длина последовательности
len(a)                    ## 5
len(b)                    ## 3
len(c)                    ## 3

Последовательности можно копировать: s * n.

>>> a = 'Hello'
>>> a * 3
'HelloHelloHello'
>>> b = [1, 2, 3]
>>> b * 2
[1, 2, 3, 1, 2, 3]
>>>

Последовательности одного и того же типа можно конкатенировать: s + t.

>>> a = (1, 2, 3)
>>> b = (4, 5)
>>> a + b
(1, 2, 3, 4, 5)
>>>
>>> c = [1, 5]
>>> a + c
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate tuple (not "list") to tuple

Слайсинг

Слайсинг означает получение подпоследовательности из последовательности. Синтаксис: s[start:end]. Где start и end - это индексы подпоследовательности, которую вы хотите получить.

a = [0,1,2,3,4,5,6,7,8]

a[2:5]    ## [2,3,4]
a[-5:]    ## [4,5,6,7,8]
a[:3]     ## [0,1,2]
  • Индексы start и end должны быть целыми числами.
  • Слайсы не включают конечное значение. Это похоже на полуоткрытый интервал из математики.
  • Если индексы опущены, они по умолчанию равны началу или концу списка.

Перезапись среза

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

## Перезапись
a = [0,1,2,3,4,5,6,7,8]
a[2:4] = [10,11,12]       ## [0,1,10,11,12,4,5,6,7,8]

Примечание: Перезаписанный срез не обязательно должен иметь ту же длину.

## Удаление
a = [0,1,2,3,4,5,6,7,8]
del a[2:4]                ## [0,1,4,5,6,7,8]

Сокращение последовательностей

Существует несколько общих функций для сокращения последовательности до одного значения.

>>> s = [1, 2, 3, 4]
>>> sum(s)
10
>>> min(s)
1
>>> max(s)
4
>>> t = ['Hello', 'World']
>>> max(t)
'World'
>>>

Итерация по последовательности

Цикл for итерируется по элементам последовательности.

>>> s = [1, 4, 9, 16]
>>> for i in s:
...     print(i)
...
1
4
9
16
>>>

На каждой итерации цикла вы получаете новый элемент для работы. Это новое значение помещается в переменную итерации. В этом примере переменной итерации является x:

for x in s:         ## `x` - это переменная итерации
  ...statements

На каждой итерации предыдущее значение переменной итерации перезаписывается (если оно было). После завершения цикла переменная сохраняет последнее значение.

break - оператор

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

for name in namelist:
    if name == 'Jake':
        break
  ...
  ...
statements

Когда выполняется оператор break, цикл завершается, и выполнение продолжается с следующих statements. Оператор break действует только на самый вложенный цикл. Если этот цикл находится внутри другого цикла, он не остановит внешний цикл.

continue - оператор

Для пропуска одного элемента и перехода к следующему используйте оператор continue.

for line in lines:
    if line == '\n':    ## Пропустить пустые строки
        continue
    ## Дальнейшие инструкции
 ...

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

Цикл по целым числам

Если вам нужно подсчитывать, используйте range().

for i in range(100):
    ## i = 0,1,...,99

Синтаксис: range([start,] end [,step])

for i in range(100):
    ## i = 0,1,...,99
for j in range(10,20):
    ## j = 10,11,..., 19
for k in range(10,50,2):
    ## k = 10,12,...,48
    ## Обратите внимание, как оно считает с шагом 2, а не 1.
  • Конечное значение никогда не включается. Это отображает поведение срезов.
  • start является необязательным. По умолчанию 0.
  • step является необязательным. По умолчанию 1.
  • range() вычисляет значения по мере необходимости. Он не фактически хранит большой диапазон чисел.

Функция enumerate()

Функция enumerate добавляет дополнительное значение счетчика к итерации.

names = ['Elwood', 'Jake', 'Curtis']
for i, name in enumerate(names):
    ## Цикл с i = 0, name = 'Elwood'
    ## i = 1, name = 'Jake'
    ## i = 2, name = 'Curtis'

Общая форма: enumerate(sequence [, start = 0]). start является необязательным. Хороший пример использования enumerate() - отслеживание номеров строк при чтении файла:

with open(filename) as f:
    for lineno, line in enumerate(f, start=1):
     ...

В конце концов, enumerate - это просто удобный шорткат для:

i = 0
for x in s:
    statements
    i += 1

Использование enumerate требует меньше набираемых символов и работает немного быстрее.

For и кортежи

Вы можете выполнять итерацию с несколькими переменными итерации.

points = [
  (1, 4),(10, 40),(23, 14),(5, 6),(7, 8)
]
for x, y in points:
    ## Цикл с x = 1, y = 4
    ##            x = 10, y = 40
    ##            x = 23, y = 14
    ##           ...

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

Функция zip()

Функция zip принимает несколько последовательностей и создает итератор, который объединяет их.

columns = ['name','shares', 'price']
values = ['GOOG', 100, 490.1 ]
pairs = zip(columns, values)
## ('name', 'GOOG'), ('shares',100), ('price',490.1)

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

for column, value in pairs:
  ...

Одним из распространенных применений zip является создание пар ключ/значение для построения словарей.

d = dict(zip(columns, values))

Упражнение 2.13: Счет

Попробуйте некоторые базовые примеры подсчета:

>>> for n in range(10):            ## Считаем 0... 9
        print(n, end=' ')

0 1 2 3 4 5 6 7 8 9
>>> for n in range(10,0,-1):       ## Считаем 10... 1
        print(n, end=' ')

10 9 8 7 6 5 4 3 2 1
>>> for n in range(0,10,2):        ## Считаем 0, 2,... 8
        print(n, end=' ')

0 2 4 6 8
>>>

Упражнение 2.14: Более операции с последовательностями

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

>>> data = [4, 9, 1, 25, 16, 100, 49]
>>> min(data)
1
>>> max(data)
100
>>> sum(data)
204
>>>

Попробуйте выполнить цикл по данным.

>>> for x in data:
        print(x)

4
9
...
>>> for n, x in enumerate(data):
        print(n, x)

0 4
1 9
2 1
...
>>>

Иногда конструкция for, len() и range() используются новичками в каком-то ужасном фрагменте кода, который выглядит, словно вывалился из глубин старого生锈的 C-программа.

>>> for n in range(len(data)):
        print(data[n])

4
9
1
...
>>>

Не делайте так! Не только чтение этого кода вызывает отвращение у всех, он неэффективен по памяти и работает значительно медленнее. Просто используйте обычный цикл for, если хотите выполнить итерацию по данным. Используйте enumerate(), если по какой-то причине вам нужен индекс.

Упражнение 2.15: Практический пример enumerate()

Помните, что файл missing.csv содержит данные о портфеле акций, но некоторые строки имеют пропущенные данные. С использованием enumerate() модифицируйте программу pcost.py так, чтобы она выводила номер строки вместе с предупреждением, когда встречает плохие данные.

>>> cost = portfolio_cost('/home/labex/project/missing.csv')
Строка 4: Не удалось преобразовать: ['MSFT', '', '51.23']
Строка 7: Не удалось преобразовать: ['IBM', '', '70.44']
>>>

Для этого вам нужно изменить несколько частей кода.

...
for rowno, row in enumerate(rows, start=1):
    try:
     ...
    except ValueError:
        print(f'Строка {rowno}: Плохая строка: {row}')
✨ Проверить решение и практиковаться

Упражнение 2.16: Использование функции zip()

В файле portfolio.csv первая строка содержит заголовки столбцов. Везде в предыдущем коде мы просто игнорировали их.

>>> f = open('/home/labex/project/portfolio.csv')
>>> rows = csv.reader(f)
>>> headers = next(rows)
>>> headers
['name','shares', 'price']
>>>

Однако, что если бы вы могли использовать заголовки для чего-то полезного? Именно здесь функция zip() приходит на помощь. Сначала попробуйте это, чтобы сопоставить заголовки файла с одной строкой данных:

>>> row = next(rows)
>>> row
['AA', '100', '32.20']
>>> list(zip(headers, row))
[ ('name', 'AA'), ('shares', '100'), ('price', '32.20') ]
>>>

Заметьте, как zip() сопоставляет заголовки столбцов с значениями столбцов. Здесь мы использовали list(), чтобы превратить результат в список, чтобы вы могли его увидеть. Обычно zip() создает итератор, который должен быть использован в цикле for.

Это сопоставление является промежуточным этапом для создания словаря. Теперь попробуйте это:

>>> record = dict(zip(headers, row))
>>> record
{'price': '32.20', 'name': 'AA','shares': '100'}
>>>

Это преобразование является одним из самых полезных приемов, которые стоит знать при обработке большого количества файлов с данными. Например, предположим, что вы хотели, чтобы программа pcost.py работала с различными входными файлами, не зависимо от того, в каком столбце фактически находятся имя, количество акций и цена.

Модифицируйте функцию portfolio_cost() в pcost.py так, чтобы она выглядела так:

## pcost.py

def portfolio_cost(filename):
 ...
        for rowno, row in enumerate(rows, start=1):
            record = dict(zip(headers, row))
            try:
                nshares = int(record['shares'])
                price = float(record['price'])
                total_cost += nshares * price
            ## Это ловит ошибки в преобразованиях int() и float() выше
            except ValueError:
                print(f'Строка {rowno}: Плохая строка: {row}')
 ...

Теперь, попробуйте вашу функцию на совершенно другом файле с данными portfoliodate.csv, который выглядит так:

name,date,time,shares,price
"AA","6/11/2007","9:50am",100,32.20
"IBM","5/13/2007","4:20pm",50,91.10
"CAT","9/23/2006","1:30pm",150,83.44
"MSFT","5/17/2007","10:30am",200,51.23
"GE","2/1/2006","10:45am",95,40.37
"MSFT","10/31/2006","12:05pm",50,65.10
"IBM","7/9/2006","3:15pm",100,70.44
>>> portfolio_cost('/home/labex/project/portfoliodate.csv')
44671.15
>>>

Если вы сделали это правильно, вы обнаружите, что ваша программа по-прежнему работает, даже если файл с данными имеет совершенно другой формат столбцов, чем раньше. Это круто!

Изменение, сделанное здесь, незаметно, но существенно. Вместо того, чтобы portfolio_cost() был жестко закодирован для чтения одного фиксированного формата файла, новая версия читает любой CSV-файл и выбирает из него интересующие значения. Пока файл имеет нужные столбцы, код будет работать.

Модифицируйте программу report.py, которую вы написали в разделе 2.3, так, чтобы она использовала ту же технику для выбора заголовков столбцов.

Попробуйте запустить программу report.py на файле portfoliodate.csv и убедитесь, что она дает ту же ответ, что и раньше.

✨ Проверить решение и практиковаться

Упражнение 2.17: Обратное отображение словаря

Словарь сопоставляет ключи значениям. Например, словарь цен на акции.

>>> prices = {
        'GOOG' : 490.1,
        'AA' : 23.45,
        'IBM' : 91.1,
        'MSFT' : 34.23
    }
>>>

Если использовать метод items(), можно получить пары (ключ, значение):

>>> prices.items()
dict_items([('GOOG', 490.1), ('AA', 23.45), ('IBM', 91.1), ('MSFT', 34.23)])
>>>

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

>>> pricelist = list(zip(prices.values(),prices.keys()))
>>> pricelist
[(490.1, 'GOOG'), (23.45, 'AA'), (91.1, 'IBM'), (34.23, 'MSFT')]
>>>

Зачем это делать? Во-первых, это позволяет выполнять определенные виды обработки данных над данными словаря.

>>> min(pricelist)
(23.45, 'AA')
>>> max(pricelist)
(490.1, 'GOOG')
>>> sorted(pricelist)
[(23.45, 'AA'), (34.23, 'MSFT'), (91.1, 'IBM'), (490.1, 'GOOG')]
>>>

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

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

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

>>> a = [1, 2, 3, 4]
>>> b = ['w', 'x', 'y', 'z']
>>> c = [0.2, 0.4, 0.6, 0.8]
>>> list(zip(a, b, c))
[(1, 'w', 0.2), (2, 'x', 0.4), (3, 'y', 0.6), (4, 'z', 0.8))]
>>>

Также обратите внимание, что zip() останавливается, как только исчерпан самая короткая входная последовательность.

>>> a = [1, 2, 3, 4, 5, 6]
>>> b = ['x', 'y', 'z']
>>> list(zip(a,b))
[(1, 'x'), (2, 'y'), (3, 'z')]
>>>

Резюме

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