Основы программирования в Python

Алла Тамбовцева, НИУ ВШЭ

План лекции основан на лекциях Щурова И.В., курс «Программирование на языке Python для сбора и анализа данных» (НИУ ВШЭ).

Методы .split() и .join(), списковые включения

Метод .split()

Мы уже достаточно хорошо знакомы с вводом какой-то информации с клавиатуры с помощью input(). Однако раньше мы всегда вводили с клавиатуры только один объект: одно число или одно слово... А можно ли вводить сразу несколько слов или чисел? Конечно, можно! Посмотрим.

In [1]:
heroes = input("Введите имена героев мюзикла Notre Dame de Paris: ")
Введите имена героев мюзикла Notre Dame de Paris: Гренгуар Фролло Эсмеральда

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

In [2]:
heroes
Out[2]:
'Гренгуар Фролло Эсмеральда'

А теперь разобьем эту строку по пробелу, чтобы получить четыре отдельных имени – воспользуемся методом .split().

In [3]:
heroes_list = heroes.split() # по умолчанию деление по пробелу
heroes_list
Out[3]:
['Гренгуар', 'Фролло', 'Эсмеральда']

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

In [4]:
heroes_list[1]
Out[4]:
'Фролло'

Разбивать строку на части можно по любому символу, достаточно указать нужный символ в скобках в .split().

In [5]:
fheroes = input("Введите имена любимых героев NDDP через запятую: ")
Введите имена любимых героев NDDP через запятую: Фролло,Гренгуар
In [6]:
fheroes
Out[6]:
'Фролло,Гренгуар'
In [7]:
fheroes.split(",")
Out[7]:
['Фролло', 'Гренгуар']

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

In [8]:
nums = input("Введите два числа: ")
nums_list = nums.split()
Введите два числа: 1999 2018

Список получили, но он пока состоит из строк, не из чисел.

In [9]:
nums_list
Out[9]:
['1999', '2018']

Поправим: превратим все элементы списка в целые числа с помощью цикла и сохраним их в новый список.

In [10]:
final = []

for n in nums_list:
    final.append(int(n))
final
Out[10]:
[1999, 2018]

Чуть позже мы узнаем, как более удобно (и что приятно, более быстро) сделать то же самое, но без цикла.

А пока узнаем, можно ли разбить строку по пустоте? То есть просто разделить её на отдельные символы. Попробуем.

In [11]:
"1234".split("") # нельзя
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-11-ab6b88668550> in <module>
----> 1 "1234".split("") # нельзя

ValueError: empty separator

Python сообщает нам, что разделитель пустой, и делить строку на части не хочет. Однако это возможно с помощью функции list(), при применении к строке она возвращает список символов в строке:

In [12]:
list("1234")
Out[12]:
['1', '2', '3', '4']

Раз есть метод для разбиения строки на список строк, должна быть и обратная операция – для склеивания списка строк в одну большую строку. И такой метод действительно есть!

Метод .join()

Пусть у нас есть список, состоящий из имени и фамилии.

In [13]:
l = ['Daniel', 'Lavoie']

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

In [14]:
" ".join(l) # вуаля
Out[14]:
'Daniel Lavoie'

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

In [15]:
"_".join(l)
Out[15]:
'Daniel_Lavoie'

Важно: метод .join() берет в качестве аргумента именно список, не просто перечисленные элементы через запятую.

In [16]:
a = '7'
b = '8'

" ".join(a,b) # не работает
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-16-e664b268447f> in <module>
      2 b = '8'
      3 
----> 4 " ".join(a,b) # не работает

TypeError: join() takes exactly one argument (2 given)
In [17]:
" ".join([a, b]) # работает
Out[17]:
'7 8'

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

In [18]:
date = input("Введите дату: ") 
date
Введите дату: 20.04.2018
Out[18]:
'20.04.2018'
In [19]:
parts = date.split(".")
result = "-".join(parts)
print(result)
20-04-2018

Метод .strip()

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

In [20]:
# вот так

fheroes = input("Введите имена любимых героев NDDP через запятую: ")
fheroes
Введите имена любимых героев NDDP через запятую: Фролло, Гренгуар
Out[20]:
'Фролло, Гренгуар'

Как быть? Разбить строку по запятой, а потом избавиться от пробела. Избавиться от пробела поможет метод .strip().

In [21]:
hero = " Гренгуар"
hero.strip()
Out[21]:
'Гренгуар'

Метод .strip() работает по-умному: он убирает пробелы в начале и в конце строки, пробелы в середине (между словами) никуда не денутся.

In [22]:
heroes = "  Гренгуар  Фролло "
heroes.strip()
Out[22]:
'Гренгуар  Фролло'

На самом деле это не случайно. Если посмотреть на все методы для строк, то можно увидеть целых три варианта со .strip():

  • .lstrip();
  • .rstrip();
  • .strip().

Метод .lstrip() убирает все пробелы в начале строки (слева, l – от left), метод .rstrip() – все пробелы в конце строки (справа, r – от right), а метод .strip()является их объединением, то есть убирает все пробелы в начале и в конце строки.

In [23]:
"  Гренгуар  ".lstrip()
Out[23]:
'Гренгуар  '
In [24]:
"  Гренгуар  ".rstrip()
Out[24]:
'  Гренгуар'
In [25]:
"  Гренгуар  ".strip()
Out[25]:
'Гренгуар'

Списковые включения

Сейчас мы познакомимся со списковыми включениями (list comprehensions) – выражения, которые позволяют создавать новые списки на основе старых, без создания пустого списка и заполнения его в цикле. Чтобы понять, что такое списковые включения, лучше сначала посмотреть на пример. Попробуем создать список, состоящий из элементов другого списка, возведённых в квадрат.

In [26]:
old = [1, 0, 3, 5]
new = [i**2 for i in old]
new
Out[26]:
[1, 0, 9, 25]

Цикл for у нас по-прежнему задействован, только немного в ином качестве и в другой последовательности: сначала мы указываем, что нужно делать с элементами, а потом – по каким элементам будем пробегаться. В нашем примере мы говорим, что возводим элементы i в квадрат, а потом сообщаем, что эти элементы i мы берём из списка old.

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

In [28]:
snumbers = input("Введите числа через пробел: ")
lnumbers = snumbers.split()

numbers = [int(n) for n in lnumbers] # делаем все элементы целочисленными
numbers
Введите числа через пробел: 5 6 9
Out[28]:
[5, 6, 9]

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

In [29]:
%%timeit

# timeit - магическое слово Jupyter, magic command,
# которое позволяет измерить время исполнения ячейки с кодом
# ячейка запускается много раз, и выводится среднее время выполнения со стандарным отклонением
# должно быть указано первой строчкой в ячейке

L = []
for i in range(1, 10001):
    L.append(i**2)
2.8 ms ± 61.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [30]:
%%timeit

L = [j**2 for j in range(1, 10001)]
2.39 ms ± 11.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

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

In [31]:
names = input("Введите имена героев через запятую: ")
lnames = names.split(",")
names_final = [name.strip() for name in lnames] # берем все элементы и отсекаем пробелы
names_final # готово!
Введите имена героев через запятую: Квазимодо, Фролло, Феб
Out[31]:
['Квазимодо', 'Фролло', 'Феб']