C++ и Python

Язык Python

Основы синтаксиса языка python

Язык python прост в изучении, хорошо документирован и очень популярен. Ответы на большинство вопросов касающихся python могут быть найдены с помощью поисковых запросов в вашей любимой поисковой системе.

Этот раздел представляет собой набор примеров, которые показывают основные средства языка python.

Основные типы данных

Встроенные типы являются сильной стороной языка python. Они очень удобны в использовании и предоставляют множество встроенных методов.

Арифметические типы

В python есть три арифметических типа: int, float и complex:

a = 4
type(a)                  # int
b = 4.
type(b)                  # float
c = 5 + 6 * (2**5) / 7   # float 32.42857142857143
d = 5 + 6 * (2**5) // 7  # int 32
e = 2 + 3j
type(e)                  # complex
type(e.real)             # float
e.conjugate()            # (2-3j)

Тип int обладает произвольной точностью:

print(2**256)
# 115792089237316195423570985008687907853269984665640564039457584007913129639936

Тип float на большинстве платформ имеет размер 64 бита.

Логический тип и объект None

Логический тип bool представлен значениями True и False. Логические выражение можно составлять с помощью операторов and, or и not:

True or False        # True
2 < 3 and 3 < 5      # True
2 < 3 and not 5 < 3  # True
2 == 2               # True
3 != 2               # True
a = True
a is True            # True

В последнем примере использован оператор is, который проверяет идентичность объектов, т.е. ссылаются ли объекты на одну и ту же область памяти. Оператор is не проверяет равенство:

a = 3.1415
b = 3.1415
a == b  # True
a is b  # False

Объект None обозначает отсутствие значения и может использоваться в логических выражениях:

None == None  # True
None is None  # True
not None      # True
bool(None)    # False

Строки

Строки в python представлены типом str

s = 'Hello'
type(s)  # str

Длину строки можно получить с помощью встроенной функции len

len(s)  # 5

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

'Hello, ' + 'world!'

Тип str имеет большое количество встроенных методов. Вот некоторые примеры:

'Hello'.beginswith('Hel')  # True
'Hello'.endswith('llo')    # True
'123'.isdigit()            # True
'123.12'.isdigit()         # False
'abs1'.isalpha()           # False
'abs'.isalpha()            # True
'    123   '.strip()       # '123'
'   123  456   789    '.strip().split()
# ['123', '456', '789']
'/'.join(['/home', 'vitaly', 'python_lecture.ipynb'])
# /home/vitaly/python_lecture.ipynb

Выполнить поиск по строке можно с помощью оператора in и метода find:

'll' in 'Hello'     # True
'Hello'.find('ll')  # 2

Со строкой можно работать как с массивом символов. Обращение по индексу и выбор диапазона элементов массива в python выполняется весьма удобно:

a = 'apple'
a[1]       # 'p'
a[0:3]     # 'app'
a[:3]      # 'app'
a[-1]      # 'e'
a[-3:]     # 'ple'
sorted(a)  # ['a', 'e', 'l', 'p', 'p']

В последнем примере мы воспользовались встроенной функцией sorted и получили отсортированный массив символов.

Преобразование строки в арифметические типы выполняется очевидным образом:

int('123')
float('123.45')
complex('123+45j')

Контейнеры

Язык python содержит четыре встроенных контейнера:

  • list — список
  • tuple — кортеж
  • set — множество
  • dict — словарь

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

Тип list

Создадим list:

l1 = [1, 2, 3]
l2 = [1, '2', 3+4j]

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

l2d = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

Объекты list — изменяемые (в отличие от str и tuple):

lst = []
lst.append(1)     # [1]
lst += [5, 6, 5]  # [1, 5, 6, 5]
lst.remove(5)     # [1, 6, 5]
lst

Поиск элемента в списке:

l = [43, 55, 98]
55 in l  # True
12 in l  # False

Обращение к элементам списка по индексу:

arr = list(range(10))  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
arr[3]      # 3
arr[-3]     # третий элемент с конца: 7
arr[2:6]    # диапазон со второго по шестой: [2, 3, 4, 5]
arr[:4]     # диапазон от начала до четвертого: [0, 1, 2, 3]
arr[-4:]    # диапазон от четвертого с конца до конца: [6, 7, 8, 9]
arr[1:6:2]  # диапазон от первого до шестого с шагом 2: [1, 3, 5]
arr[::3]    # каждый третий элемент: [0, 3, 6, 9]
arr[::-3]   # каждый третий элемент, начиная с конца: [9, 6, 3, 0]
arr[::-1]   # все элементы списка в обратном порядке

Эти примеры демонстрируют гибкость индексирования в python. После некоторой практики индексирование становится очень простым и удобным инструментом.

Встроенная функция sorted позволяет получить копию списка, содержащую отсортированные элементы. Метод sort выполняет сортировку исходного списка:

a = [3, 2, 5, 6, 1]
sorted(a)  # [1, 2, 3, 5, 6]
print(a)   # [3, 2, 5, 6, 1]
a.sort()   # сортируем список in place
print(a)   # [1, 2, 3, 5, 6]

Тип tuple

Тип tuple позволяет создавать неизменяемые кортежи объектов:

t = ('Mark', 31, 4.87)
type(t)  # tuple

Работать с кортежами можно также как со списками, учитывая, что кортежи неизменяемые. В частности, для них не определен метод append. При необходимости изменить кортеж придется создать новый объект.

Кортежи используются в случаях, когда структура данных известна заранее. Часто функции возвращают кортеж объектов.

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

  • Кортежи создаются быстрее, чем списки
  • Кортежи занимают меньше памяти, чем списки
  • Кортежи можно использовать в качестве ключей set и map, а списки нельзя

Тип set

Ассоциативный контейнер set позволяет работать со множеством уникальных объектов. Контейнер реализован в виде хэш-таблицы и обеспечивает добавление, удаление и поиск элементов за константное время:

s = {1, 5, 2, 3, 5, 4, 1, 'hjk'}
s.add(987)
9 in s  # False

Тип set поддерживает основные операции со множествами:

a = {1,2,3}
b = {2,3,4}
b-a    # {4}
a & b  # {2, 3}
a | b  # {1, 2, 3, 4}

Тип dict

Ассоциативный контейнер dict хранит пары ключ-значение. Как и set, dict реализован в виде хэш-таблицы и выполняет операции добавления, поиска и удаления объектов за константное время:

a = {'key1': 'val1', 'key2': 'val2'}
a['key1']  # 'val1'
a[(1,4)] = 'typle val'  # добавляем новый ключ и новое значение
a['missing_key']  # такого ключа нет, поэтому будет сгенерировано
                  # исключение IndexError
a.get('missing_key', None)  # Запросили значение для ключа
                            # 'missing_key'. Если ключ не будет найдет,
                            # то вернется None
a.update({  # добавляем сразу несколько ключей и значений
    'key3': 'val3,
    'key4': 'val4,
})
'key3' in a                 # True

Управляющие конструкции

Оператор if-elif-else:

a = 2
if a > 5:
    print('> 5')
elif a == 5:
    print('5')
elif a < 5:
    print('< 5')
else:
    print('How we get here?')

Цикл for

for i in [0, 1, 2, 3, 4, 5]:
    print(str(i)*i, end='-')
# -1-22-333-4444-55555-

for ch in 'Hello':
    print(ch, end=' ')
# H e l l o

for i in range(2,10,2):
    print(i**2, end = ' ')
# 4 16 36 64

for i in range(9):
    if i % 2:
        continue
    print(str(i)*i, end=' ')
# 22 4444 666666 88888888

Цикл while

a, b = 1, 4
while a < b:
    a += 2
    b += 1
    if b == 10:
        break

Функции

Определить функцию можно с помощью ключевого слова def:

def solve(k, b):
    """ Solves equation k*x + b = 0 """
    return -b / k

solve(1, -2)  # 2.0

К коментарию функции можно обратиться через метод __doc__:

solve.__doc__ # ' Solves equation k*x + b = 0 '

Лямбда-функции

В python очень часто используют лямбда-функции — анонимные функции, которые могут содержать только одно выражение. С помощью лямбда-функций можно управлять поведением функции sorted:

a = [3, 2, 5, 6, 1]
sorted(a)  # [1, 2, 3, 5, 6]
sorted(a, key=lambda x: (x - 3)**2)  # [3, 2, 5, 1, 6]

При сортировке во втором случае сравнивались не сами элементы массива, а их значения, преобразованные с помощью лямбда-функции. Мы еще не раз будем использовать лямбда-функции в различных ситуациях.

Встроенные функции

В python определены некоторые встроенные функции. Встроенные функции len, range и sorted мы уже встречали. Функцией print, которая передает данные в стандартный поток вывода, мы начали пользоваться без особых пояснений. Вот несколько примеров использования других функций:

abs(-4.1 + 2.1j)  # возвращает модуль числа: 4.606...
sum([1, 3, 5, 7])  # (16) возвращает сумму элементов
all([True, True, False])
# (False) возвращает True, если все элементы равны True
any([True, True, False])
# (True) возвращает True, если хотя бы один элемент равен True
callable('a')
# (False) возвращает True, если объект является вызываемым
callable(lambda x: x**2)  # True
isinstance(1, int)
# (True) проверяет является ли объект экземпляром класса
round(3.141592653589793, 4)  # 3.1416

Функции enumerate, reversed и zip весьма полезны при работе с циклами:

arr = ['a', 'b', 'c']

for idx, item in enumerate(arr):
    print(f'{idx}: {item},'), end=' ')
# Выведет:
# 0: a, 1: b, 2: c

for item in reversed(arr):
    print(item, end=', ')
# Выведет:
# c, b, a

arr2 = ['d', 'e', 'f']
for it1, it2 in zip(arr, arr2):  # идем синхронно по двум спискам
    print(f'{it1}{it2}', end=' ')
# Выведет:
# ad be cf

Форматированные строки (f-строки), использованные в последних примерах, будут подробно рассмотрены в другом разделе.

Функция input позволяет получать данные из стандартного потока ввода:

s1 = input()
s2 = int(input('Enter an integer number: '))
# Переданная строка будет выведена в поток вывода

Аргументы функции

В python очень гибкая система объявления и передачи аргументов функций. Рассмотрим различные примеры. Определим функцию, принимающую два аргумента и возвращающую разность их значений:

def f1(a, b):
    return a - b

Вот эквивалентные способы вызова этой функции:

f1(1, 2)       # -1 — позиционные аргументы
f1(a=1, b=2)   # -1 — именованные аргументы
# не имеет значения порядок передачи именованных аргументов
f1(b=2, a=1)   # -1
args = [1, 2]  # список аргументов для передачи в функцию
f1(*args)      # -1
# словарь, ключи которого соответствуют именам аргументов функции
kwargs = {a: 1, b: 2}
f1(**kwargs)   # -1

Можно задать значение аргумента по умолчанию:

def f2(a, b=5):
    return a - b

Функцию f2 можно вызывать с одним аргументом

f2(1)  # -4

Можно объявить произвольное количество аргументов функции:

def f3(*args):
    return sum(args)

f3(3, 4, 5, 6)  # 18
args = [3, 4, 5, 6]
f3(*args)       # 18

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

def f4(**kwargs):
    """ Prints names, types, and values of the passed arguments """
    print('Here are my arguments:')
    for name, val in kwargs.items():
        print(f'{name} ({type(val)}): {val}')

f4(a=1, s='string', l=['a', 2+3j])
# Here are my arguments:
# a (<class 'int'>): 1
# s (<class 'str'>): string
# l (<class 'list'>): ['a', (2+3j)]

Возможно комбинация трех способов объявления аргументов:

def f5(a, b, *args, **kwargs):
    pass

Функция f5 имеет два обязательных аргумента, после которых может идти произвольное количество позиционных аргументов. После этого в функцию можно еще передать произвольные именованные аргументы.

Работа с файлами

Чтение и запись в файл происходит с помощью функции open:

with open('text.txt', 'w') as f:
    f.write('An important message.')

with open('text.txt', 'r') as f:
    s = f.read()
    print(s)  # 'An important message.`

При чтении файла можно итерироваться по его строкам:

with open('text.txt', 'r') as f:
    for line in f:
        pass

При необходимости записи новых данных в конец файла, если он уже существует, вместо ключа w необходимо использовать a. Для чтения или записи данных в бинарном формате необходимо использовать ключ b, например: wb или rb.

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

Оператор map и list/set/dict comprehension

Списки в python можно создавать с помощью специальной конструкции, которую называют списковым включением (list comprehension).

Вместо

squares = []
for i in range(10):
    squares.append(i**2)

можно написать

squares = [i**2 for i in range(10)]

Помимо краткости, эта конструкция исполняется эффективнее, чем вызов метода append в цикле.

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

squares = list(map(lambda x: x**2, range(10)))

Схожим образом можно создавать множества и словари:

s = {i**2 for i in range(10)}  # set
d = {i: i**2 for i in range(10)}  # dict

В списковых включениях можно использовать условный оператор для фильтрации объектов:

l = [i**2 for i in range(10) if i % 3 == 0]  # [0, 9, 36, 81]

Множественное присваивание и распаковка

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

a, b, c = 1, 'a', [3, 2, 1]

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

a, b = b, a

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

t = (1988, 'Mike', 4.56)
year, name, score = t

Можно распаковывать и итерируемые объекты с произвольным количеством объектов:

a, b, *c, d = range(10)
# a = 0
# b = 1
# c = [2, 3, 4, 5, 6, 7, 8]
# d = 9

Вместо резюме

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

Источники