Sidebar LogoHomeIndexTODOЗадачиIndexGitHub </> Back Next Элементы программы на языке Python

Элементы программы на языке Python


Соглашение об оформлении кода

Язык Python предъявляет достаточно жесткие требования к оформлению программного кода, однако все равно вариантов написания может быть достаточно много, и это может вызвать проблемы в понимании кода разными программистами. Для повышения степени единообразия кода были разработаны соглашения, которые предъявляют дополнительные требования к оформлению кода. Несоблюдение этих требований не приведет к ошибке при выполнении программы, однако их соблюдение сделает код более единообразным и, следовательно, более понятным для всех.

См. PEP 8 – Style Guide for Python Code.


Комментарии

Комментарий начинается с символа #, они игнорируются интерпретатором и нужны для пояснения программистам или тем, кто читает код:

>>> print("Hello, world!")  # этот текст будет проигнорирован
Hello, world!

Типы данных и значения

Значение — это элемент данных, которым манипулирует программа. Любое значение каким-то образом представлено в памяти. В программе они указываются непосредственно (в виде литералов) или появляются в результате вычислений (вычисления будет рассмотрены позже).

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

Далее будут рассмотрены различные типы данных языка Python.

Скалярные значения

Скалярные значения — это такие значения, которые воспринимаются как единичные, т.е. неделимые. Рассмотрим основные типы таких значений.

Числовые значения:

Логические значения. Имеются только два логических значения: True и False, т.е. "истина" и "ложь".

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

Note

Пустые значения подобные None присутствуют и в других языках программирования. Например, в языке Джава типом переменной может быть "строка", и эта переменная может иметь значение (nole), при этом тип все равно будет строка. В Puthon None это отдельный тип.

Комплексные значения

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

- строка — последовательность символов. В программном коде задаются литералами с использованием одинарных либо двойных кавычек, например: "a", "b", "23", 'A, "Z", s', "A, 'Z', s". В строке могут использоваться специальные последовательности: - \": "Ф, П, \"р\", т" — двойная кавычка внутри строки; - \': 'Ф, П, \'р\', т' — одинарная кавычка внутри строки; - \\ — один слеш; - \n — перевод строки. - коллекции — это комплексные значения, которые содержат изначально неопределенное количество других значений. К ним относятся: - списки - кортежи - словари - множества - строки, которые тоже можно рассматривать как коллекции, однако ввиду их особой важности они были вынесены в отдельный пункт.
- классы — составной тип данных с предопределенной структурой.

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

Другие типы значений

Есть другие типы значений, например функции и генераторы. Они будут рассмотрены отдельно.

Определение типа значения

Чтобы проверить тип любого объекта в Python, используется функция type():

>>> type(5)
<class 'int'>
>>> type(3.6)
<class 'float'>
>>> type("demo")
<class 'str'>
>>> type(False)
<class 'bool'>
>>> type(None)
<class 'NoneType'>
>>> type([1, 2, 3])
<class 'list'>

type() здесь является примером использования функции. Функции мы рассмотрим позже. Также здесь приведен пример список, который также будет рассмотрен отдельно.

Преобразование типов

Значение одних типов могут быть преобразованы в значения некоторых других типов, например: число в строку, строка в число. Такое преобразование может выглядеть тривиальным, однако на самом деле значения различных типов имеют различные представления в памяти, поэтому преобразование требует фактического пересчета бинарного представления значений. Например: строка '1' в однобайтной кодировке ASCII представляется как число 4910 или 001100012. При этом число 1 представленное 1 байтом будет 000000012.

Note

В языке Puthon для кодирования чисел и символов как правило используется больше одного байта. В приведенном выше примере для простоты использовалось представление размером в 1 байт. Этого достаточно для того, чтобы передать основную идею преобразования типов данных.

>>> float(1)  # convert from int to float
1.0
>>> int(2.8)  # convert from float to int
2
>>> str(1)
'1'
>>> int('2')
2

Приведение значений к логическому типу

Конструкции языка Python, требующие для своей работы логических выражений/значений, такие как условия if и while, могут также принимать выражения/значения других типов, которые интерпретируются как логические. Например:

>>> if 1+1:
...     print('yes')
yes

Note

Условные оператор if и цикл while будут рассмотрены позже. Здесь важно понимать только то, что от выражения 1+1 ожидается логический результат.

Казалось бы, условному оператору требуется логический аргумент для принятия решения, однако в данном примере мы видим, что в данном случае оператор if интерпретировал значение 2 как True. Особенностью языка Python является то, что в нем значения разных типов могут приводиться к соответствующему логическому значению. Основные правила таковы:

Тип False True
число 0 все остальные
строка пустая строка не пустая строка
коллекция пустая коллекция не пустая коллекция
None всегда никогда

По аналогии с функциями int() и str() есть функция bool, которая возвращает логическое представление значений различных типов. Рассмотрим примеры ее работы:

Тип Выражение Результат
число bool(0) False
число bool(0.0000001) True
число bool(-1) True
строка bool('') False
строка bool('a') True
строка bool('0') True
строка bool('False') True
логический bool([False]) True
список * bool([]) False
список bool([5, 'a']) True
словарь * bool({}) False
словарь bool({1:'s'}) True
множество * bool(set()) False
множество bool(set([5,a])) True
None bool(None) False

* — данные коллекции будут рассмотрены позже.

Интерпретация логических значений как чисел

Логические значения в языке Python могут интерпретироваться как числа: True как 1, False как 0.

>>> True + True
2
>>> True + False
1
>>> False + False
0
>>> s = [10, 20]
>>> s[False]
10
>>> s[True]
20

Здесь упоминается индексная адресация списков, которая будет рассмотрена здесь


Переменные

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

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

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

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

Рассмотрим понятие переменной с точки зрения архитектуры вычислительного устройства, безотносительно языка программирования. На уровне машинного кода понятия переменной не существует. Аналогичный функционал может быть реализован с использованием некоторой области памяти (назовем ее ссылкой), в которой находится не само значение, а адрес этого значения, где-то в другом месте памяти.

В языке Python, так же принято рассматривать переменные как ссылки. На самом деле в официальной документации они называются не переменными, а именами.

Имена переменных

Переменная имеет имя и указывается с использованием этого имени, например: a, d, x1 или более описательные: age, name, total_amount.

Правила для имен переменных Python:

myvar = "John"
my_var = "John"
_my_var = "John"
myVar = "John"
MYVAR = "John"
myvar2 = "John"

Использование переменных

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


Операции

Операции нужны для выполнения действий над значениями. Среди всего множества операций языка Python можно выделить следующие:

Цепочка сравнений

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

x < y <= z эквивалентно x < y and y <= z

В примере выше y вычисляется только один раз. Если x < y оказывается ложным, то в обоих случаях, приведенных выше z не оценивается вообще.

Еще пример:

a < b <= c < d эквивалентно a < b and b <= c and c < d

Рассматривая это выражение как последовательность операций, то независимо от приоритетов операции == и in получаем:

Поскольку это цепочка сравнения, то данное выражение False == False in [False] эквивалентно (False == False) and (False in [False]), которое возвращает True.

Более подробно об этих и других операциях можно посмотреть здесь.

"Моржовый" оператор

Неплохое объяснение можно найти здесь.

Note

Данный оператор появился в версии Python 3.8. Интерпретаторы более ранних версий будут считать использование этого оператора ошибкой.

"Моржовый" оператор := (англ. walrus operator) выполняет присвоение переменной значения внутри выражения. Например:

>>> 1 + (a := 5) * a
26

Здесь выражение a := 5 определяет переменную a и присваивает ей значение 5. Сам оператор := возвращает присвоенное значение, в данном случае 5. Определенная переменная может быть использована далее в выражении.

Другой пример: вывести введенное с клавиатуры число, его квадрат и куб.

>>> print(n := int(input()), n ** 2, n ** 3)
3
3 9 27

Без оператора := потребовалось бы вводить число в отдельной строке.


Выражения

Выражение — это конструкция состоящая из:

Пример выражения: 20 + Х - len(a).

Выражение в программе вычисляется и формируется значение, которое может быть присвоено переменной либо участвовать в вычислении других выражений. Указатель значения либо переменной, так же является выражением. Например: 5, Х, "Х".

Значение которое заданно непосредственно без вычислений называется литералом. Например: "аbc", 25.3.

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

max(2 + 2, len([0, 0] * 3))

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

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

Второй аргумент предоставляет собой вызов функции len с аргументом, заданным выражением, которое, в свою очередь, представляет собой умножение списка (заданного литералом) на число (тоже заданное литералом). Элементы списка так же заданны литералами. Результатом умножения будет список из шести нулей. Следовательно, функция len вернет 6.

В конечном счете функция max будет вызвана с аргументами 4 и 6 и вернет 6.


Ветвление

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

t = 38
if t < 37:  # оператор условия *if* со значением *t < 37*
    print('здоров')  #  тело условия
else: # "альтернативная ветка" 
    print('болен')   

Программа напечатает "болен".


Циклы

Цикл — это управляющая конструкция, которая позволяет выполнять последовательность операций несколько раз. Два вида циклов: цикл по условию while и цикл по счетчику for.

Цикл по условию

Тело цикла выполняется до тех пор, пока условие верно.

i = 3
while i > 0:
    print(i)
    i = i - 1

Программа выведет : 1, 2, 3.

Цикл по счетчику

Цикл по счетчику выполняется по элементам итерируемого объекта. Итерируемым объектом может быть массив, из него можно последовательно получать (читать) элементы.

a = [1, 2, 3]
for x in a:
    print(x)

Цикл for последовательно выбирает элементы списка а и присваивает их переменной х, которую можно использовать в теле цикла. Этот цикл можно назвать синтаксический сахар, потому что все тоже самое можно сделать с помощью цикла while.

Список является итерируемым объектом, поэтому его можно использовать в операторе for.

Другим вариантом итерируемого объекта является последовательность. В математике ее могут называть прогрессией. Функция range() является генератором, представляет арифметическую прогрессию и принимает аргументы:

  1. начальное значение (включительно);
  2. конечное значение (не включительно);
  3. приращение (шаг), по умолчанию +1.

Если указать один аргумент, то последовательность будет от 0 до указанного числа с шагом +1.

for i in range(2, 8, 2):
    print(i)

Данная программа напечатает 2, 4, 6.

for i in range(3):
    print(i)

Данная программа напечатает 0, 1, 2.

Блок else

Также в циклах while и for можно указать else-блок после основного блока цикла. else-блок в циклах выполняется один раз после завершения работы цикла, но только в случае, если цикл не был прерван инструкцией break.

    n=input("Введите целое число, больше 1 ") # 1 Считали число n
     for d in range(2, n):                     # 2 d меняется от 2 до n-1
         if n % d == 0:                        # 3 Проверка: n делится на d?
             print n, "имеет делитель", d      # 4
             break                             # 5 Завершаем цикл for
     else:                                     # 6 Это else-блок цикла
         print n, "простое число"              # 7

Циклы c предусловием и постусловием

Циклы for и while являются циклами с предусловием, т.е. условие проверяется перед началом итерации и возможно такая ситуация, когда тело цикла не выполняется ни разу. (В случае с циклом for это происходит, если итерируемый объект пустой.) В цикле с постусловием условие проверяется по окончанию итерации и поэтому как минимум одна итерация будет выполнена. В языке Python нет специального синтаксиса для циклов с постусловием. Однако они могут быть реализованы следующим образом. Например есть задача: получать числа с клавиатуры и считать сумму положительных чисел, при вводе отрицательного числа прекратить выполнение программы.

sum = 0
while True:
    a = int(input())
    if a < 0:
        break
    sum += a
print(sum)

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

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

for i in 1, 2, 3, 'one', 'two', 'three':
    print(i)

Итерируемые объекты

Итерируемые объекты — объекты позволяющие получать последовательность значений. К таким объектам относятся:

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

>>> for i in {'a', 'b', 'c'}:
...     print(i)
...
a
c
b

Функция zip() является интересным примером. Она принимает итерируемые объекты и возвращает генеротор (который также являются итерируемым объектом). Пример:

>>> for i, j in zip(range(1, 4), ['яблоки', 'груши', 'сливы']):
...     print(i, j)
...
1 яблоки
2 груши
3 сливы

В данном примере функция zip() принимает два итерируемых объекта, генератор и список, и возвращает последовательность пар элементов (кортеж). В цикле for данные значения присваиваются переменным цикла i и j.


Функция

Функция в python - это объект, принимающий аргументы и возвращающий значение. Функция обеспечивает инкапсуляцию программного кода, т.е. предоставляет единую точку входа и избавляет пользователя функции от необходимости знать особенности ее реализации. Обычно функция определяется с помощью инструкции def. Инструкция return говорит, что нужно вернуть значение. Чтобы вызвать функцию, используйте имя функции, за которым следуют круглые скобки. Термины параметр и аргумент могут считаться синонимами обозначающими значения, которые передаются в функцию.

def bubble_sort(a):

По умолчанию функция должна вызываться с правильным количеством аргументов. Это означает, что если ваша функция ожидает 2 аргумента, вы должны вызывать функцию с 2 аргументами, не больше и не меньше. Если вы не знаете, сколько аргументов будет передано в вашу функцию, добавьте *перед именем параметра в определении функции. Аргументы приходят извне и внутри функции могут использоваться так же, как и локальные (внутри функции) переменные. Аргументы, которые передаются в функцию через запятую, называются позиционными. Существуют и именнованные параметры, которые имеют имена. Например:

print (1, 'a', True, sep=',', end= '|')

В данном примере в функцию print передаются три позиционных аргумента (1, 'a', True) и два именнованных аргумента (sep=',', end= '|'), они указываются всегда после позиционных.

Особенности функций как объектов первого класса:

def print_field(string, transform):
    print('[' + transform(string) + ']')

def simple_transform(string):
    return string   # Что пришло, то функция и вернула

def password_transform(string):
    return '*' * len(string)  # Возвращает звездочки

print_field("I love my cat", simple_transform)
print_field("my secret", password_transform)

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

[I love my cat]
[*********]

Другой пример использования функции как объекта можно посмотреть здесь.

В функцию print_field можно передавать анонимную функцию используя lambda-выражение.

print_field("I love my cat", lambda s: s)
print_field("my secret", lambda s: '*' * len(s))

В данном случае lambda-выражение позволяет нам определить функцию на месте, т.е. в той точке кода, где требуется ее указать. По сравнению с предыдущим вариантом, дополнительных функций (simple_transform и password_transform) создавать не нужно. Результат выполнения будет такой же точно.

Другой пример использования lambda-выражений смотри здесь.

Функция может возвращать результат. Для этого в функции используется оператор return, после которого указывается возвращаемое значение:

1. def get_message():
1.   return "Hello METANIT.COM  # return возвращаемое значение.

message = get_message()  # получаем результат функции get_message в переменную message
print(message)          # Hello METANIT.COM

# можно напрямую передать результат функции get_message
print(get_message())    # Hello METANIT.COM

Оператор return не только возвращает значение, но и производит выход из функции. Поэтому он должен определяться после остальных инструкций.

yield — это ключевое слово в Python, которое используется для возврата из функции с сохранением состояния ее локальных переменных, и при повторном вызове такой функции выполнение продолжается с оператора yield, на котором ее работа была прервана. Любая функция, содержащая ключевое слово yield, называется генератором. Можно сказать, yield — это то, что делает ее генератором.

def fibonacci(n):
    """функция-генератор возвращает последовательность Фибоначчи""" 
    f1 = -1
    f2 = 1
    for i in my_range(0, n, 1):
        f3 = f1 + f2 
        yield f3 
        f1 = f2
        f2 = f3
Преимущества yield:

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

Недостатки yield:

Иногда использование yield может вызвать ошибки, особенно если вызов функции не обрабатывается должным образом. За оптимизацию времени работы и используемой памяти приходится платить сложностью кода, поэтому иногда трудно сходу понять логику, лежащую в его основе.

Функция input() в Python временно приостанавливает выполнение программы и ожидает, пока пользователь введёт данные. Как только функция получает данные от пользователя, Python автоматически заносит их в переменную, чтобы с ними было удобно работать. Функция input() получает только один аргумент и это текст подсказки, который выводится на экран, чтобы пользователь понимал что от него требуется.

X = int(input())
Y = int(input())
print(X*60 + Y)
5
20
320

Функция input() всегда возвращает строковое значение, даже числа возвращаются в виде строковых символов. Функция int() принимает текстовое представление целого числа и возвращает соответствующее число. Например:

>>> print(int('0017'))
17

Функция sqrt() – это встроенная функция, которая возвращает квадратный корень из любого числа.

a = int(input())
b = int(input()) 
c = int(input())
p = ((a + b + c) / 2)
s = (p * (p-a) * (p-b) * (p-c)) ** (1/2)
print(s)

Функции с переменным количеством аргументов

В языке Python функции могут иметь фиксированное количество аргументов. Например, функция input() принимает 0 или 1 аргумент:

>>> print(input('23',' hjk'))
TypeError: input expected at most 1 argument, got 2 (функция не может принимать больше 1 аргумента)

Однако есть функции, которые могут принимать различное количество аргументов, например функция print() печатает переданное количество аргументов с заданным разделителем. Рассмотрим пример написания подобной функции.

def minimum(*args):
    if len(args) < 1:
        return None
    min_valiu = args[0]
    for v in args:
        if v < min_valiu:
            min_valiu = v
    return min_valiu

print(minimum()) 
print(minimum(42)) 
print(minimum('b', 'a')) 
print(minimum(2, 3, 5, 0, 1)) 
>python delete.py
None
42
a
0

Функция minimum может принимать разное количество аргументов, в том числе ноль. В списке аргументов такой функции следует указать * перед именем аргумента. При этом при вызове функции все переданные ей аргументы представляются как единое значение типа список.

Important

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


Распаковка коллекций в аргументы при вызове функции

Бывают ситуации, когда есть функция принимающая определенное количество аргументов, и есть коллекция, содержащая соответствующее количество элементов. Например, функция range() может принимать три аргумента и, предположим, есть такая коллекция:

>>> a = [1, 6, 2]

Вызвать функцию range() с аргументами из этого списка можно так:

>>> for i in range(a[0], a[1], a[2]):
...     print(i)
...
1
3
5

В языке Python есть более короткий способ с использованием так называемой распаковки коллекций (англ. spread operator), которая преобразует коллекцию в отдельные аргументы при вызове функции. Применяется путем добавления * перед указанием коллекции:

>>> for i in range(*a):
...     print(i)
...
1
3
5

Как можно видеть, результат тот же самый!

Important

Данное использование символа * отличается от его использования при определении функции (см. здесь). В данном случае мы функцию не определяем, а вызываем.

Вот еще один пример использования:

a = sorted([int(input()) for _ in range(3)], reverse=True)
print(*a, sep="\n") 

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

Note

На самом деле использование распаковки здесь необязательно. Функция min может принимать итерируемый объект в качестве единственного аргумента.


Коллекции

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

Массив

Массив — это сложное (композитное) значение, состоящее из идущих подряд значений, которые называются элементами массива.

x = 10
a = [1, 'hi', 1 + 1, x] # В данном примере массив *а* содержит элементы: [`1`, `hi`, `2`, `10`]

Элементы массива имеют порядковые номера, которые называются индексами. Индексы — это последовательные целые числа начинающиеся с нуля. Доступ к элементу массива осуществляется по индексу следующим образом: выражение a[0] вернет выражение 1; после операции a[2] = 2, третий по порядку элемент массива будет являться 2. Для массивов определена функция len, которая возвращает длину массива. Выражение len(а)вернет значение 4.

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

Вложенные списки или матрицы

Вложенные списки — это когда в качестве элемента одного списка, являются другие списки. Если все вложенные списки имеют одинаковую длину, то такой список может представлять прямоугольную таблицу или матрицу.

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

5 2 7
3 -6 8
-1 4 9
>>> m = [[5, 2, 7], [3, -6, 8], [-1, 4, 9]]
>>> m
[[5, 2, 7], [3, -6, 8], [-1, 4, 9]]

Мы можем обратиться к любому элементу из этой матрицы по индексам m[i][j].

>>> m[0]
[5, 2, 7]
>>> m[0][0]
5
>>> m[1]
[3, -6, 8]
>>> m[2][1]
4

Ссылочная модель вложенных списков

Пусть имеем вложенный список созданный следующим образом:

>>> a = [[0] * 3]

В памяти это будет выглядеть следующим образом (поз. 1):

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

>>> a = [[0] * 3] * 3

При этом память будет выглядеть как на рисунке, поз. 2, т.е. элементы внешнего списка будут ссылаться на один и тот же список. При этом возможна следующая ситуация:

>>> a[0][0] = 1
>>> a
[[1, 0, 0], [1, 0, 0], [1, 0, 0]]

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

Чтобы создать действительно разные списки (как на рисунке в поз. 3) можно использовать средство генерации списков (list comprehension):

>>> a = [[0] * 3 for i in range(3)]
>>> a[0][0] = 1
>>> a
[[1, 0, 0], [0, 0, 0], [0, 0, 0]]

Кортеж

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

def get_tuple():
    return 5, "с", True  # возвращает кортеж из трех элементов разного типа.
    # функция заканчивает свою работу.

А, В, С = get_tuple()
print(А, В, С)

Словарь

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

>>> d={'dog': 'собака', 'cat': 'кот', 'human': 'человек'}
>>> d
{'dog': 'собака', 'cat': 'кот', 'human': 'человек'}

В этом примере мы создали словарь d с тремя элементами. Рассмотрим основные операции со словарями.

Поиск по ключу

>>> d['cat']
'кот'
>>> d['cow']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'cow'

Поиск по ключу 'cat' вернул соответствующее значение, а поиск по ключу 'cow' вызвал ошибку, потому что элемента с таким ключом в словаре нет. Возможен другой способ поиска, при котором для отсутствующего ключа возвращается пустое значение:

>>> print(d.get('cow'))
None

Удаление элемента

>>> del d['human']
>>> d
{'dog': 'собака', 'cat': 'кот'}

Important

Если удаляемый ключ отсутствует, то операция удаления вызовет ошибку KeyError как в предыдущем примере.

Добавление элемента

>>> d['cow'] = 'корова'
>>> d
{'dog': 'собака', 'cat': 'кошка', 'cow': 'корова'}

Изменение элемента

>>> d['cat'] = 'кошка'
>>> d
{'dog': 'собака', 'cat': 'кошка', 'cow': 'корова'}

Note

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

Итерация по словарю

Имеется словарь d:

>>> d
{'dog': 'собака', 'cat': 'кошка', 'cow': 'корова'}

Требуется выполнить с каждым элементом некоторые действия. Для простоты будем печатать ключи и значения. Существует три способа итерации.

Также см. цикл for.

По ключам

>>> for k in d.keys():
...     print(k)
...
dog
cat
cow
key = input().split()
new_data = {i:user[i] for i in key}
print(new_data)

Метод d.keys() возвращает список ключей словаря d как итерируемый объект. Следовательно, этот объект может быть использован в цикле for. На каждой итерации переменная цикла k получает значение очередного ключа.

Идентичным образом работает выражение for k in d:.

По значениям

>>> for v in d.values():
...     print(v)
...
собака
кошка
корова

По парам ключ-значение

>>> for k, v in d.items():
...     print(k, v)
...
dog собака
cat кошка
cow корова 

Метод d.items() возвращает список кортежей из двух элементов, ключа и значения. Поэтому используются две переменные цикла, k и v.

Методы словаря

setdefault() — добавляет пару ключ-значение только в том случае, если такого ключа нет.

>>> d = {"a": 1, "b": 2}
>>> d.setdefault("a", 10)
1
>>> d
{'a': 1, 'b': 2}
>>> d.setdefault("c", 3)
3
>>> d
{'a': 1, 'b': 2, 'c': 3}

pop() — возвращает значение, находящееся под указанным ключом, а из самого словаря удаляется пара с данным ключом. Вызов метода без указания ключа, либо же без существующего ключа, приводит к ошибке TypeError.

popitem() — удалит и вернет двойной кортеж (key, value) из словаря. Пары возвращаются с конца словаря, в порядке LIFO (последним пришёл - первым ушёл). При попытке удаления элементов из пустого словаря возникает ошибка KeyError.

Множества

Множества в математике представляют набор элементов и предполагают такие операции как: объединение, пересечение, вычитание. Более подробно см. в Википедии.

В языке Python множество (объект типа set) представляет набор уникальных значений:

>>> s = {1, 2, 2, 3, 198}
>>> s
{1, 2, 3, 198}

Как видно из примера, конструктор непустого множества отличается от конструктора словаря тем, что в нем перечисляются только значения без ключей. Конструктор пустого множества — set():

>>> s = set()
>>> s.add(50)
>>> s.add(10)
>>> s.add(30)
>>> s.add(30)
>>> s
{50, 10, 30}

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

Рассмотрим некоторые другие операции. Определение наличия элемента в множестве:

>>> 10 in s
True
>>> 20 in s
False

Пересечение (&) множеств:

>>> {1, 2, 5} & {2, 3, 6, 5}
{2, 5}

Объединение (|) множеств:

>>> {1, 2, 5} | {2, 3, 6, 5}
{1, 2, 3, 5, 6}

Разность (-) множеств:

>>> {1, 2, 5} - {2, 3, 6, 5}
{1}

Генерация коллекций

Рассмотрим простой способ генерирования списка, например, квадратов чисел от 1 до 7:

>>> lst = []
>>> for i in range(1, 8):
...     lst.append(i * i)
...
>>> lst
[1, 4, 9, 16, 25, 36, 49]

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

>>> lst = [i * i for i in range(1, 8)]
>>> lst
[1, 4, 9, 16, 25, 36, 49]

List comprehension работает подобно циклу for, только направляет значения переменной цикла в генерируемый список. Подобным способом можно генерировать другие виды коллекций.

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

>>> tuple(i // 2 for i in lst if i % 2 == 0)
(2, 8, 18)

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

>>> {i: f"{i:b}" for i in lst}
{1: '1', 4: '100', 9: '1001', 16: '10000', 25: '11001', 36: '100100', 49: '110001'}

Сгенерируем множество из некоторого произвольного списка.

>>> set(i for i in [2, 2, 1, 1, 3])
{1, 2, 3}

Функция которая принимает на вход список целых чисел, удаляет из него все нечётные значения, а чётные нацело делит на два. Можно для решения использовать list comprehension.

def modify_list(numbers):
    numbers[:] = [i // 2 for i in numbers if i % 2 == 0]

Генерация коллекций с вложенным циклом

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

colors = ['White', 'Blue', 'Yellow']
sizes = ['S', 'M']
print([(c, s) for c in colors for s in sizes])

Результат будет такой:

[('White', 'S'), ('White', 'M'), ('Blue', 'S'), ('Blue', 'M'), ('Yellow', 'S'), ('Yellow', 'M')]

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

vector = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12], [13, 14, 15], [16, 17, 18]]
print([num for row in vector for num in row])

Результат будет такой:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]

Анонимный генератор

Генерация коллекций (list comprehension) создает объект, который занимает какое-то место в памяти. Потребление памяти составляет О(N).

>>> [i*2 for i in range(5)]
[0, 2, 4, 6, 8]

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

>>> (i*2 for i in range(5))
<generator object <genexpr> at 0x0000016E82F79BA0>

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

g =(input() for _ in range(2))
>>> g = (i*2 for i in range(5))
>>> sum(g)
20

Точно также можно было бы использовать list comprehension (с квадратными скобками), однако в этом случае был бы создан список с умноженными на два числами от 0 до 4, который после подсчета суммы нам больше не требуется. При использовании генератора (с круглыми скобками) умножение на 2 происходит "на лету", и полученные элементы нигде не накапливаются. Потребление памяти константно, О(1).

Можно посчитать сколько цифр в строке.

print(sum(i.isdigit() for i in input()))

В этом примере используется интерпретация логических значений как чисел.

Пример использования можно также посмотреть и здесь.

Рассмотрим еще один пример.

В случае с list comprehension возвращается список, поэтому все действия (в том числе ввод с клавиатуры) выполняются до завершения формирования списка.

>>> a = [input() for _ in range(2)]
a
b
>>> a
['a', 'b']

Для доказательства отложенной работы генератора рассмотрим следующий пример:

>>> g =(input() for _ in range(2))
>>> 

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

>>> for s in g: print('-->', s)
...
a
--> a
c
--> c
>>>

Модуль

Модуль в программировании — это независимая и функционально законченная часть программы, оформленная в виде фрагмента кода и сохраненная в отдельный файл. Чтобы использовать модуль в другой программе мы его импортируем (import).

Более подробно смотри здесь.


Объекты и классы

Простые, "примитивные" типы данных описывают одно значение. Объекты реального мира моделируемые в программе могут иметь несколько атрибутов различных типов. Например, книга в каталоге может быть описана следующими атрибутами:

Классы- это сложные (композитные) типы данных. Класс можно представить как шаблон, на основе которого создаются (инстанцируются) объекты, которые иногда называются экземплярами класса. Представьте чертеж дома. В нем содержится вся информация: сколько этажей, какого размера двери, окна и т.д. На основе это чертежа мы можем построить дом. Дом — это объект. По одному чертежу можно построить несколько домов. Так же и с классом — по нему можно создать много объектов. Объект также можно назвать экземпляром класса. Например, следующий класс представляет сущность "Книга".

class Book:
    def __init__(self, author, title, year, page_count):  # функция __init__ это конструктор класса

    # при вызове метода объекта нам обязательно необходимо использовать слово self
    # для обращения к атрибутам объекта внутри класса в его методах также применяется слово self:
        self.author = author  # это одно из полей класса
        self.title = title
        self.year = year
        self.page_count = page_count

    def colofon(self):
        return (self.author + ' "' + self.title + '", ' + str(self.year) + ' г., ' +  
        str(self.page_count) + ' c.')


Объект — это набор данных (переменных) и методов (функций), которые с этими данными взаимодействуют. Объект также можно назвать экземпляром класса, а процесс его создания — инстанцированием. Пример:

book1 = Book('А. Блок', 'Ветер', 1975, 45) # выражение, создающее экземпляр класса Book
print(book1.author)
print(book1.title)
print(book1.colofon()) 

Программа выведет строки: "А. Блок", "Ветер".


Обработка ошибок

Ошибки в программировании часто называют исключениями, англ. exception.

При возникновении ошибки в программе, Python прекращает выполнение программы и отображает сообщение об ошибке.

x = 0
print(f 'x = {x}')  # (1)
y = 5 / x           # (2)
print(y)            # (3)
>python hello.py
x = 0
Traceback (most recent call last):
  File "hello.py", line 3, in <module>
    y = 5 / x
ZeroDivisionError: division by zero

В нашем примере (строка 1) была выполнена, в (строке 2) произошла ошибка, поэтому остальная часть программы (строка 3) не была выполнена. В данном случае сообщение об ошибке было выведено в консоль.

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

x = 0
print(f'x = {x}')   # (1)
if x == 0:
    y = 0
else:
    y = 5 / x          # (2)
print(f'y = {y}') 
>python hello.py
x = 0
y = 0

Такие решения должны быть тщательно продуманы и согласованы с бизнес-требованиями. Возможно, что в подобной ситуации заказчик ожидает, что программа аварийно завершит работу. Тогда данное решение приведет к сокрытию ошибки, которая может потом проявиться в других частях программы. В языке Python есть специальный способ обработки ошибок.

x = 0
print(f'x = {x}')   # (1)
try: 
    y = 5 / x 
except ZeroDivisionError:
    y = 0       # (2)
print(f'y = {y}') 
>python hello.py
x = 0
y = 0

Блок try - except (защищенный блок) определяет фрагмент программы, в котором ошибки не будут сразу вызывать аварийное завершение всей программы. В блоке except есть возможность каким-то образом устранить последствия ошибки и/или бросить тоже самое или другое исключение.

В данном примере указана конкретная ошибка ZeroDivisionError, которая будет "отлавливаться", если конкретную ошибку не указать, то будут отлавливаться все ошибки.

В следующей задаче вводится массив чисел и некоторое число x. Требуется вывести все индексы числа x в массиве. Если таковых нет, то вывести "None".

lst = [int(i) for i in input().split()]
x = int(input())
i = -1
while True:
    try:
        i = lst.index(x, i + 1)
    except:
        if i < 0:
            print("None")
        break
    print(i, end=' ')

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


Отступы

В языке Python отступы используются для структурных частей программы. В отличии от многих других языков программирования, в ввиду отсутствия операторных скобок, отступы и переводы строк являются значимыми элементами программного кода. То есть изменения отступа изменяет поведение программы. Рассмотрим пример. Следующая программа определяет являются ли элементы списка частью арифметической прогрессии.

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

def is_range(sek):                             # (1)
    if len(sek) < 3:                           # (2)
        return 'NO'                            # (3)
    delta = sek[1] - sek[0]                    # (4)
    for i in range(2, len(sek)):               # (5)
        if sek[i]- sek[i - 1] != delta:        # (6)
            return 'NO'                        # (7)
    return 'YES'                               # (8)

print(is_range([2, 5, 8, 11]))                 # (9)

(1) — начинается определение функции. Нулевой отступ говорит о том, что это будет обязательно выполнено при исполнении модуля ( файла в котором находится этот код). Код в строках (2...8) является подчиненным к строке (1). Поэтому строки (1...8) можно условно считать одной ( хотя и составной) инструкцией. Следующая инструкция в таком случае, можно считать код в строке (9).

(2) — один отступ ( 4 пробела). Первая инструкция внутри функции, будет обязательно исполнена при вызове функции.

(3) — тело условия, один отступ внутри родительского (условного) блока, всего два отступа. Данная инструкция return, будет выполнена только при определенном условии.

(4) — один отступ. т. е. можно считать следующим оператором после if (2). В данном случае он может не выполниться, если предыдущее условие окажется верным и выполнение функции завершится. Однако если выполнение функции попадет в строку (4), то эта строка точно будет выполнена.

Возможные ошибки

Если в строке (4) добавить еще один отступ то, она окажется в блоке if после оператора return, что в данном случае не имеет смысла потому что после оператора return функция завершает выполнение.

К тому же, если условие окажется ложным то (4) строка не выполнится. Следовательно переменная delta не будет определена, что приведет к ошибке ее использования в строке (6).

(5) — один отступ, цикл точно будет выполнен после строки (4).

(6, 7) — тело цикла, один отступ внутри родительского блока for, итого соответственно 2 и 3 отступа. Здесь рассуждения те же самые, что и внутри строк (3, 4).

(8) — один отступ, может быть выполнена после завершения цикла for. В данном случае внутри цикла может произойти возврат значения и, соответственно, завершения работы функции. В этом случае строка (8) выполнена не будет.

Возможные ошибки

Если добавить строке (8) еще два отступа, то ошибка будет аналогична предыдущей. Только в данном случае ошибки в выполнении не произойдет и программа вместо ответа "Yes" выдаст ответ None. А это явно не то что нам нужно.

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

(9) — нулевой отступ, будет обязательно выполнена при исполнении текущего модуля (если, конечно, определение функции пройдет без ошибок).


Методы строк

Поиск и замена

Методы поиска и замены строк внутри других строк.

Каждый метод в этой группе поддерживает необязательные аргументы и . Как и в строковых срезах действие метода ограничено частью исходной строки, начинающейся с позиции символа и продолжающейся вплоть до позиции символа , но не включающей ее. Если параметр указан, а параметр нет, то метод применяется к части исходной строки от до конца строки. Если параметры не заданы, то подразумевается, что = 0 , = len(s).

Функция Значение Результат
count() считает количество непересекающихся вхождений подстроки в исходную строку
startswith() определяет начинается ли исходная строка s подстрокой prefix True/False
endswith() определяет оканчивается ли исходная строка s подстрокой suffix True/False
find() находит индекс первого вхождения подстроки sub в исходной строке s -1/ индекс
rfind() он ищет первое вхождение подстроки начиная с конца строки s, если нет то -1/ индекс
strip() возвращает копию строки s у которой удалены все пробелы стоящие в начале и конце строки
lstrip() возвращает копию строки s у которой удалены все пробелы стоящие в начале строки
rstrip() возвращает копию строки s у которой удалены все пробелы стоящие в конце строки
replace() возвращает копию s со всеми вхождениями подстроки old, замененными на new


s = 'abcdefghij'

Программный код Результат Пояснение
s[2:5] cde строка состоящая из символов с индексами 2, 3, 4
s[:5] abcde первые пять символов строки
s[5:] fghij строка состоящая из символов с индексами от 5 до конца
s[-2:] ij последние два символа строки
s[:] abcdefghij вся строка целиком
s[1:7:2] bdf строка состоящая из каждого второго символа с индексами от 1 до 6
s[::-1] jihgfedcba строка в обратном порядке, так как шаг отрицательный

S.isdigit() Состоит ли строка из цифр

S.isalpha() Состоит ли строка из букв

S.isalnum() Состоит ли строка из цифр или букв

S.islower() Состоит ли строка из символов в нижнем регистре

S.isupper() Состоит ли строка из символов в верхнем регистре

S.isspace() Состоит ли строка из неотображаемых символов (пробел, символ перевода страницы ('\f'), "новая строка" ('\n'), "перевод каретки" ('\r'), "горизонтальная табуляция" ('\t') и "вертикальная табуляция" ('\v'))

S.istitle() Начинаются ли слова в строке с заглавной буквы

S.upper() Преобразование строки к верхнему регистру

S.lower() Преобразование строки к нижнему регистру

S.join(список) Сборка строки из списка с разделителем S

ord(символ) Символ в его код ASCII

chr(число) Код ASCII в символ

S.capitalize() Переводит первый символ строки в верхний регистр, а все остальные в нижний

S.center(width, [fill]) Возвращает отцентрованную строку, по краям которой стоит символ fill (пробел по умолчанию)

S.count(str, [start],[end]) Возвращает количество непересекающихся вхождений подстроки в диапазоне [начало, конец] (0 и длина строки по умолчанию)

S.expandtabs([tabsize]) Возвращает копию строки, в которой все символы табуляции заменяются одним или несколькими пробелами, в зависимости от текущего столбца. Если TabSize не указан, размер табуляции полагается равным 8 пробелам

S.partition(шаблон) Возвращает кортеж, содержащий часть перед первым шаблоном, сам шаблон, и часть после шаблона. Если шаблон не найден, возвращается кортеж, содержащий саму строку, а затем две пустых строки

S.rpartition(sep) Возвращает кортеж, содержащий часть перед последним шаблоном, сам шаблон, и часть после шаблона. Если шаблон не найден, возвращается кортеж, содержащий две пустых строки, а затем саму строку

S.swapcase() Переводит символы нижнего регистра в верхний, а верхнего – в нижний

S.title() Первую букву каждого слова переводит в верхний регистр, а все остальные в нижний

S.zfill(width) Делает длину строки не меньшей width, по необходимости заполняя первые символы нулями

S.ljust(width, fillchar=" ") Делает длину строки не меньшей width, по необходимости заполняя последние символы символом fillchar

S.rjust(width, fillchar=" ") Делает длину строки не меньшей width, по необходимости заполняя первые символы символом fillchar

Регулярные выражения

Википедия: Регулярные выражения:

Регулярные выражения (англ. regular expressions) — формальный язык, используемый в компьютерных программах, работающих с текстом, для поиска и осуществления манипуляций с подстроками в тексте, основанный на использовании метасимволов.

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

>>> import re
>>> txt = "The rain in Spain"
>>> pattern = re.compile("^The.*Spain$")
>>> re.search(pattern, txt)
<re.Match object; span=(0, 17), match='The rain in Spain'>

В данном примере используются следующие метасимволы:

Остальные символы в данном примере обозначают сами себя.

Рассмотрим простой пример замены:

>>> pattern = re.compile("[ .,-]")
>>> txt = "  I love - my cat, ."
>>> re.sub(pattern, '', txt)
'Ilovemycat'

В данном примере используется еще один метасимвол — [], который обозначает один из перечисленных символов. Здесь мы выполняем удаление (т.е. замену на пустую строку) всех пробелов и некоторых знаков препинания.

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

Немного модифицированный вариант использования см. здесь.

 


Back Next