Операции

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

Содержание лекции

Определение операции

Операция - это некоторое действие над переменными и константами в языке программирования, зачастую аналогичное соответствующей математической операции. Операция принимает на вход один или несколько аргументов, называемых также операндами, и возвращает некоторый результат. Например, в операции a + 1 операндами являются переменная a и константа 1. Результатом, возвращаемым данной операцией, будет их сумма.

По количеству принимаемых аргументов операции в языке программирования Python делятся на две группы:

  1. унарные - имеют один аргумент, например операция "минус", возвращающая аргумент, взятый с противоположным знаком
  2. бинарные - имеют два аргумента, например операция сложения, возвращающая сумму двух аргументов

Для каждой операции определены типы данных, которые могут быть использованы для ее операндов. Если операция вызывается с переменной или константой неподдерживаемого типа, интерпретатор генерирует исключение TypeError. Ниже, когда будут подробно рассматриваться имеющиеся в Python операции, мы будем обращать ваше внимание на то, какие типы могут иметь их операнды. Как правило, эти ограничения естественны и интуитивно понятны. Рассмотрим такой пример:

In [1]:
s1 = 'hello'
s2 = 'world'
s1 / s2
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-1-826caff4b057> in <module>()
      1 s1 = 'hello'
      2 s2 = 'world'
----> 3 s1 / s2

TypeError: unsupported operand type(s) for /: 'str' and 'str'

В этом примере мы пытаемся выполнить операцию / (деление) над переменными с типами str. Очевидно, что деление строки на строку лишено смысла, поэтому интерпретатор отказывается выполнять такой код и генерирует исключение.

По сути после выполнения операции (например, a + 1), в том месте, где она была в программе, вместо нее оказывается некоторая переменная, создаваемая самим интерпретатором и содержащая результат операции. Эта внутренняя переменная, как и любая другая, имеет свой тип данных, который определяется исходя из следующего:

  1. какая операция была вызвана
  2. какие типы данных были у ее операндов

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

In [2]:
a = 1
b = 2
type(a + b) # посмотрим, какой тип имеет результат сложения двух целых чисел
Out[2]:
int

Арифметические операции

Рассматриваемые в данном разделе операции могут применяться для операндов с числовыми типами данных, например int, float, complex и Decimal. Для большинства из представленных операций результат будет иметь тот же тип данных, что и тип операндов, участвующих в ней. Исключением является операция деления /, при выполнении которой с целочисленными операндами (тип int) результат будет иметь тип float.

Название
Синтаксис Описание
Сложение
x + y
Складывает число x и число y
Вычитание
x - y
Вычитает из числа x число y
Умножение
x * y
Умножает число x на число y
Деление
x / y
Делит число x на число y
Целочисленное деление
x // y
Делит число x на число y, отбрасывая дробную часть
Получение остатка (mod)
x % y
Возвращает остаток от деления числа x на число y
Возведение в степень
x ** y
Возводит число x в степень y
Минус
-x
Изменяет знак числа x

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

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

  1. возведение в степень **
  2. минус -
  3. умножение/деление *, /, //, %
  4. сложение/вычитание +, -

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

In [3]:
a = 1 - 3 / 3 + 3 * 4 + -2 ** 2 / 2 
a
Out[3]:
10.0

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

In [4]:
a = 1 + 2 * 3
b = (1 + 2) * 3
a, b
Out[4]:
(7, 9)

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

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

In [5]:
a = 1 - (3 / 3) + (3 * 4) + ((-(2 ** 2)) / 2)
a
Out[5]:
10.0

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

Для всех бинарных арифметических операций существуют комбинированные инструкции присваивания, имеющие вид "x op= y", где вместо op стоит конкретная операция. Комбинированные присваивания являются просто более короткой формой записи выражений вида "x = x op y":

In [6]:
a = 10
a += 1 # эквивалентно a = a + 1

b = 3
c = 5
b *= c + 1 # эквивалентно b = b * (c + 1)

a, b
Out[6]:
(11, 18)

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

In [7]:
a = 2 + 2 + 3   # эквивалентно (2 + 2) + 3
b = 2 ** 2 ** 3 # эквивалентно 2 ** (2 ** 3)
a, b
Out[7]:
(7, 256)

Битовые операции

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

По причине того, что внутри компьютера все данные имеют двоичный формат, многие языки программирования (и Python в их числе) предоставляют операции для работы непосредственно с битами. Операнды битовых операций должны иметь целочисленный тип данных (int или bool), при этом тип результата будет соответствовать типу операндов. Для всех бинарных битовых операций можно использовать комбинированные инструкции присваивания, то есть вместо "x = x op y" писать "x op= y".

Название
Синтаксис Описание
Битовое AND (И)
x & y
Для каждого бита с номером n в двоичном представлении x выполняет x(n) & y(n)
Битовое OR (ИЛИ)
x | y
Для каждого бита с номером n в двоичном представлении x выполняет x(n) | y(n)
Битовое XOR (исключающее ИЛИ)
x ^ y
Для каждого бита с номером n в двоичном представлении x выполняет x(n) ^ y(n)
Сдвиг влево
x << y
Свигает все биты в двоичном представлении x на y позиций влево
Сдвиг вправо
x >> y
Свигает все биты в двоичном представлении x на y позиций вправо
Инвертация битов
~x
Инвертирует (заменяет 0 на 1 и наоборот) все биты в двоичном представлении x

Объясним теперь, как работают битовые операции AND, OR и XOR для отдельно взятых двух битов. Это удобно делать в виде таблицы:

AND
bit1 = 0 bit1 = 1
OR
bit1 = 0 bit1 = 1
XOR
bit1 = 0 bit1 = 1
bit2 = 0 0 0 bit2 = 0 0 1 bit2 = 0 0 1
bit2 = 1 0 1 bit2 = 1 1 1 bit2 = 1 1 0

С помощью этой таблицы легко можно увидеть, что операция битового AND возвращает для двух битов 1 только в том случае, если оба они равны 1, а операция OR - если хотя бы один равен 1. Для операндов с типом bool считается, что True это 1, а False - 0.

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

  1. инвертация битов ~
  2. сдвиг влево/вправо <<, >>
  3. битовое AND &
  4. битовое XOR ^
  5. битовое OR |

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

In [8]:
a = 0b1001 
b = 0b0011

print('a = {:04b}'.format(a))
print('b = {:04b}'.format(b))
print('a & b = {:04b}'.format(a & b)) # здесь выполняется операция a & b, а затем ее результат передается функции format
print('a | b = {:04b}'.format(a | b))
print('a ^ b = {:04b}'.format(a ^ b))
a = 1001
b = 0011
a & b = 0001
a | b = 1011
a ^ b = 1010

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

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

In [9]:
MSG_CREATED =   0b000001 # сообщение создано
MSG_DELIVERED = 0b000010 # сообщение доставлено получателю
MSG_READ =      0b000100 # сообщение прочитано получателем
MSG_EDITED =    0b001000 # сообщение было отредактировано
MSG_FORWARDED = 0b010000 # сообщение было отправлено еще кому-то, кроме первоначального получателя
MSG_REPLIED =   0b100000 # на сообщение был отправлен ответ

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

In [10]:
state = 0

# ... пользователь набрал текст сообщения
state |= MSG_CREATED

# ... сообщение было доставлено
state |= MSG_DELIVERED

# ... сообщение было прочитано
state |= MSG_READ

# ... на сообщение был отправлен ответ
state |= MSG_REPLIED

print('state = {:06b}'.format(state))
state = 100111

Теперь мы очень просто можем проверять, наступили или нет для сообщения определенные события:

In [11]:
# is_delivered не равно 0 только если флаг MSG_DELIVERED установлен, т.е. сообщение доставлено
# заметим, что нас не особо интересует точное значение is_delivered, а только то, равно оно нулю или нет
is_delivered = state & MSG_DELIVERED

# is_edited не равно 0 только если флаг MSG_EDITED установлен, т.е. сообщение было отредактировано
is_edited = state & MSG_EDITED

is_delivered, is_edited
Out[11]:
(2, 0)

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

In [12]:
a = 0b0101
b = 0b1001

print('a << 1 = {:04b}'.format(a << 1))
print('b >> 2 = {:04b}'.format(b >> 2))
a << 1 = 1010
b >> 2 = 0010

Операция a << 1 сдвинула все биты в двоичном представлении переменной a на одну позицию влево, при этом дописав справа один нулевой бит. Операция b >> 2 сдвинула все биты переменной b вправо на две позиции, при этом младшие два бита b были отброшены, а слева дописаны два нулевых бита.

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

In [13]:
a = 5
b = a << 3
c = a * (2 ** 3)
b, c
Out[13]:
(40, 40)

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

Операции сравнения

Язык программирования Python предоставляет стандартный набор операций сравнения с предсказуемой семантикой. Их операторы могут иметь целочисленный тип данных (int, bool), тип данных с плавающей точкой (float, complex, Decimal), а также строковый тип данных str, для которого сравнения выполняются лексикографически (так, как принято в словарях). Результат имеет булевый тип данных bool и принимает значение True, если сравнение верно и False в противном случае. Все операции сравнения имеют одинаковый приоритет.

Название
Синтаксис Описание
Меньше
x < y
True, если x меньше y, иначе False
Меньше либо равно
x <= y
True, если x меньше или равен y, иначе False
Больше
x > y
True, если x больше y, иначе False
Больше или равно
x >= y
True, если x больше или равен y, иначе False
Равно
x == y
True, если x равен y, иначе False
Не равно
x != y
True, если x не равен y, иначе False

Мы еще раз вернемся к операциям сравнения в следующем разделе, посвященном логическим операциям, а пока ограничимся простым примером:

In [14]:
a = 0
b = 1

s1 = 'hello'
s2 = 'hello'
s3 = 'world'

b1 = True   # для булевых значений считается, что True > False
b2 = False

a < b, a >= b - 1, s1 != s2, s1 > s3, b1 > b2
Out[14]:
(True, True, False, False, True)

Логические операции

Логические операции пришли в языки программирования из таких областей науки, как дискретная математика, математическая логика и исчисление высказываний. Всего в Python три логических операции. Каждая из них в качестве аргументов принимает переменные и константы типа данных bool и возвращает результат такого же типа. Операции and и or также могут использоваться и с операндами типа int, float и complex (в этом случае их результат имеет соответствующий операндам тип), однако необходимость в этом возникает крайне редко, и мы рассмотрим этот частный случай отдельно в конце раздела.

Название
Синтаксис Описание
Логическое AND (И)
x and y
True, если x и y равны True, иначе False
Логическое OR (ИЛИ)
x or y
True, если x или y равен True, иначе False
Логическое NOT (НЕ)
not x
True, если x равен False, иначе False

По аналогии с битовыми операциями, значения логических также удобно представлять в виде таблицы (обратите внимание на связь между операциями and и &, а также or и | - по сути они идентичны, только одни работают с логическими значениями True и False, а другие - с битами 0 и 1):

AND
False True
OR
False True
NOT
False True
False False False False False True True False
True False True True True True

Приоритеты логических операций следующие (от наибольшего к наименьшему):

  1. логическое NOT not
  2. логическое AND and
  3. логическое OR or

Логические операции часто применяются вместе с операциями сравнения. Рассмотрим, например, как определить, что значение переменной попадает в определенный интервал:

In [15]:
start = 0.0
end = 100.0
point = 33.8

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

is_internal_point = point > start and point < end
is_internal_point, type(is_internal_point)
Out[15]:
(True, bool)

В выражении point > start and point < end происходит следующее:

  1. сравнивается, больше ли point чем start (результат True, потому что 33.8 > 0.0)
  2. сравнивается, меньше ли point чем end (результат True, потому что 33.8 < 100.0)
  3. выполняется логическая операция and (результат True, потому что оба ее операнда равны True)

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

In [16]:
start = 0.0
end = 100.0
point = 33.8

is_internal_point = start < point < end
is_internal_point
Out[16]:
True

Теперь давайте немного усложним пример предыдущий пример. Пусть нам теперь нужно определить, что point лежит либо в интервале от $(0;10)$, либо в интервале $(90;100)$:

In [17]:
start1 = 0.0
end1 = 10.0
start2 = 90.0
end2 = 100.0

point = 9.1

# в скобках определяем принадлежит ли point первому или второму интервалу, затем объединяем результат
# операцией or, которая дает True, если хотя бы один из операндов равен True

is_internal_point = (start1 < point < end1) or (start2 < point < end2)
is_internal_point
Out[17]:
True

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

  1. если левый операнд операции and равен False, то правый не вычисляется (что логично, так как он уже не окажет влияние на результат операции and).
  2. если левый операнд операции or равен True, то правый не вычисляется (аналогично, он уже не окажет влияние на результат операции or)

Это означает, что в примере выше сравнения в правых скобках вообще не проверялись, так как уже после вычисления результата сравнений в левых скобках оказалось, что он равен True, а значит и результат всей операции or будет True.

Как мы говорили в самом начале этого раздела, логические операции and и or можно использовать и для значений типов int, float и complex. Действуют они таким образом:

  • and возвращает 0, если один из операндов 0, или значение операнда справа, если оба операнда не 0
  • or возвращает 0, если оба операнда 0, значение левого операнда, если он не 0, иначе - значение правого операнда
In [18]:
a = 0
b = 1.0
c = -2
d = 1 + 2j

a and b, a or b, b and c, d or b, a or a
Out[18]:
(0, 1.0, -2, (1+2j), 0)

Посмотрим, какой тип имеет результат логических операций в этом случае:

In [19]:
type(a and b), type(a or b), type(b and c), type(d or b), type(a or a)
Out[19]:
(int, float, int, complex, int)

Строковые операции

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

Название
Синтаксис Описание
Конкатенация
x + y
Возвращает строку, составленную из строки x и приписанной к ее концу строки y
Дублирование
x * y
Возвращает строку x, продублированную y раз

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

In [20]:
s1 = 'hello, '
s2 = 'world'
s3 = '!'
s4 = 'result: '

s4 += s1 + s2 + s3 * 3
s4
Out[20]:
'result: hello, world!!!'

Получение срезов строк

Срезом строки (англ. slice) в языке Python называется некоторое подмножество ее символов. Операцией получения среза является операция [], которая имеет три формы записи:

  • string[ start ] - возвращает строку, содержащую один символ, находящийся на позиции start в строке string (start еще называют индексом символа)
  • string[ start : end ] - возвращает строку, содержащую все символы из интервала [start; end) в строке string
  • string[ start : end : step ] - возвращает строку, содержащую все символы из интервала [start; end) в строке string, взятые с шагом step

Значения start, end и step должны быть переменными или константами с целочисленным типом данных int. Также обратите внимание, что во второй и третьей форме записи символ с индексом end не попадает в результат (у интервала открытая правая граница)!

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

Нумерация символов

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

In [21]:
s = 'ABC DEF GHI'
s[0], s[-11], s[5], s[-6]
Out[21]:
('A', 'A', 'E', 'E')

Вторая форма записи операции взятия среза позволяет сразу получить целую подстроку:

In [22]:
s[4:7], s[-7:-4]
Out[22]:
('DEF', 'DEF')

При таком способе записи мы можем опустить любой из индексов: если не указать начальный индекс, то вместо него будет использован 0, если конечный, то вместо него будет использоваться индекс на 1 больше индекса последнего элемента (в нашем примере 10 + 1):

In [23]:
s[:5], s[5:], s[:] # последний срез возвращает всю строку
Out[23]:
('ABC D', 'EF GHI', 'ABC DEF GHI')

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

In [24]:
# получаем символы от начала и до конца указанного интервала, начиная с первого и с шагом 4 (т.е. берем
#  первый символ, "шагаем" на 4 позиции вперед, берем следующий символ и так далее, пока не выйдем за
#  границы интервала или не достигнем его конца)
s[0:7:4]
Out[24]:
'AD'

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

In [25]:
# получаем строку, состоящую из символов s[6], s[5] и s[4]
s[6:3:-1]
Out[25]:
'FED'

В третьей форме записи также можно не указывать параметры start и end. В этом случае для положительного step строка просматривается с первого символа до конца строки, а для отрицательного - с последнего и до начала:

In [26]:
s[::2], s[::-2]
Out[26]:
('ACDFGI', 'IGFDCA')

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

In [27]:
s = 'there is a miscake in the string'
s = s[:14] + 't' + s[15:] # 'there is a mis' + 't' + 'ake in the string'
s
Out[27]:
'there is a mistake in the string'

Неявные преобразования типов

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

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

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

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

В случае, если интерпретатор Python не смог произвести преобразование типов, он генерирует исключение TypeError.

Перечислим преобразования типов, считающиеся безопасными в языке программирование Python:

  1. bool -> int -> float -> complex - в арифметических операциях, операциях сравнения и некоторых других (при этом True преобразуется в 1, а False в 0)
  2. int -> bool, float -> bool, complex -> bool, str -> bool - в контекстах, где ожидается значение булевого типа (при этом любое ненулевое число или непустая строка преобразуется в True, а 0 или пустая строка - в False)

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

In [28]:
a = 2
b = 2.5
c = a * b # a преобразуется в float (int -> float)

c, type(c)
Out[28]:
(5.0, float)
In [29]:
a = 2
b = 4.6 + 8j
c = b / a # a преобразуется в complex (int -> float -> complex)

c, type(c)
Out[29]:
((2.3+4j), complex)

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

In [30]:
a = True
b = False

c = 1
d = 5.0

r1 = a + c # c преобразуется в int (bool -> int), True -> 1
r2 = b * d # b преобразуется в float (bool -> int -> float), False -> 0

r1, type(r1), r2, type(r2)
Out[30]:
(2, int, 0.0, float)

В конце обратим ваше внимание на то, что в языке программирования Python отсутствует неявное преобразование из строки в число, поэтому выполнение такого кода завершается генерацией исключения TypeError:

In [31]:
a = 10
s = '20'
a + s
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-31-abc0a29c9581> in <module>()
      1 a = 10
      2 s = '20'
----> 3 a + s

TypeError: unsupported operand type(s) for +: 'int' and 'str'

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

Вопросы для самоконтроля

  1. Что такое операнд?
  2. На какие типы в зависимости от количества аргументов разделяются операции в Python?
  3. Перечислите все унарные операции, с которыми мы познакомились в этой лекции.
  4. Что такое приоритет операции? Зачем он нужен?
  5. В чем смысл короткой схемы вычисления логических операций? Какие именно сравнения будут выполнены интерпретатором в примере In [16] в случае, если point равно -1.0?
  6. Что такое срез строки?
  7. В каких случаях интерпретатор Python может использовать неявные преобразования типов? Когда их можно назвать безопасными?

Задание

  1. Напишите программу, находящую корни квадратного уравнения $a*x^2+b*x+c=0$ для некоторых $a$, $b$ и $c$ (подсказка: $\sqrt{x}=x^\frac{1}{2}$).
  2. Используя пример про состояние сообщения, напишите программу, которая определяет, что сообщение или доставлено, или отредактировано, или и то и другое. Программа должна выводить True, если сообщение удовлетворяет перечисленным условиям, и False в противном случае.
  3. Напишите программу, которая выводит True, если некоторое слово одинаково выглядит как при написании слева направо, так и справа налево (например, "топот"), и False в противном случае.