#!/usr/bin/env python # coding: utf-8 # # Операции # В данной лекции рассказывается о том, какие операции (арифметические, логические и другие) можно выполнять над переменными и константами (литералами) в Python, какие типы они должны иметь, чтобы операцию можно было выполнить, а также какие преобразования осуществляет интерпретатор, если в одной операции используются значения разных типов. Не все операции языка Python описываются в этой лекции - некоторые будут рассмотрены позднее, когда мы познакомим читателя с аспектами языка, в рамках которых они используются. # ## Содержание лекции # * [Определение операции](#Определение-операции) # * [Арифметические операции](#Арифметические-операции) # * [Битовые операции](#Битовые-операции) # * [Операции сравнения](#Операции-сравнения) # * [Логические операции](#Логические-операции) # * [Строковые операции](#Строковые-операции) # * [Получение срезов строк](#Получение-срезов-строк) # * [Неявные преобразования типов](#Неявные-преобразования-типов) # * [Вопросы для самоконтроля](#Вопросы-для-самоконтроля) # * [Задание](#Задание) # ## Определение операции # **Операция** - это некоторое действие над переменными и константами в языке программирования, зачастую аналогичное соответствующей математической операции. Операция принимает на вход один или несколько аргументов, называемых также **операндами**, и возвращает некоторый результат. Например, в операции `a + 1` операндами являются переменная `a` и константа `1`. Результатом, возвращаемым данной операцией, будет их сумма. # По количеству принимаемых аргументов операции в языке программирования Python делятся на две группы: # 1. **унарные** - имеют один аргумент, например операция "минус", возвращающая аргумент, взятый с противоположным знаком # 2. **бинарные** - имеют два аргумента, например операция сложения, возвращающая сумму двух аргументов # Для каждой операции определены типы данных, которые могут быть использованы для ее операндов. Если операция вызывается с переменной или константой неподдерживаемого типа, интерпретатор генерирует исключение `TypeError`. Ниже, когда будут подробно рассматриваться имеющиеся в Python операции, мы будем обращать ваше внимание на то, какие типы могут иметь их операнды. Как правило, эти ограничения естественны и интуитивно понятны. Рассмотрим такой пример: # In[1]: s1 = 'hello' s2 = 'world' s1 / s2 # В этом примере мы пытаемся выполнить операцию `/` (деление) над переменными с типами `str`. Очевидно, что деление строки на строку лишено смысла, поэтому интерпретатор отказывается выполнять такой код и генерирует исключение. # По сути после выполнения операции (например, `a + 1`), в том месте, где она была в программе, вместо нее оказывается некоторая переменная, создаваемая самим интерпретатором и содержащая результат операции. Эта внутренняя переменная, как и любая другая, имеет свой тип данных, который определяется исходя из следующего: # 1. какая операция была вызвана # 2. какие типы данных были у ее операндов # Для каждой конкретной операции, рассматриваемой ниже, мы расскажем, какой тип данных имеет ее результат. При этом помните о функции `type`, с помощью которой вы сами можете легко получить эту информацию: # In[2]: a = 1 b = 2 type(a + b) # посмотрим, какой тип имеет результат сложения двух целых чисел # ## Арифметические операции # Рассматриваемые в данном разделе операции могут применяться для операндов с числовыми типами данных, например `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 # С помощью обычных скобок `()` можно указать интерпретатору, в какой последовательности выполнять операции (так же, как это делается в математике): # In[4]: a = 1 + 2 * 3 b = (1 + 2) * 3 a, b # Как видите, значение переменной `a` высчитывалось исходя из приоритетов операций (поэтому умножение выполнилось раньше сложения), а в выражении для переменной `b` мы явно указали, что вначале должно выполниться сложение. Как и в математике, выражения в скобках можно вкладывать друг в друга, при этом вычисляются они от самых внутренних скобок к наружним. # Скобки `()` следует использовать всегда, когда выражение является достаточно сложным, содержащим несколько различных операций - это упрощает чтение исходного кода, так как не нужно вспоминать, какой приоритет имеет каждая операция, входящая в выражение. Давайте с их помощью перепишем первый пример из этого раздела, сохранив порядок выполнения операций тем же: # In[5]: a = 1 - (3 / 3) + (3 * 4) + ((-(2 ** 2)) / 2) a # Наиболее сложный момент в этом выражении заключается в вычислении `-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 # В заключение отметим особенность операции возведения в степень `**`: если их несколько в одном выражении, то они выполняются не слева направо, а наоборот, справа налево: # In[7]: a = 2 + 2 + 3 # эквивалентно (2 + 2) + 3 b = 2 ** 2 ** 3 # эквивалентно 2 ** (2 ** 3) a, b # ## Битовые операции # Как известно, вся информация в компьютере хранится в [двоичном виде](https://ru.wikipedia.org/wiki/%D0%94%D0%B2%D0%BE%D0%B8%D1%87%D0%BD%D0%B0%D1%8F_%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0_%D1%81%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D1%8F), т.е. представляет из себя набор битов, каждый из которых может принимать одно из двух значений: 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` для того, чтобы выводить значения переменных в двоичном формате. Кроме того желательно, чтобы читатель имел представление о том, как десятичные числа преобразуются в двоичные и наоборот (оба эти преобразования можно найти [здесь](https://ru.wikipedia.org/wiki/Двоичная_система_счисления)). # 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)) # Часто в программировании приходится отслеживать и в нужные моменты изменять состояние некоторого процесса. Это состояние удобно описывать набором **флагов** - специальных значений, у которых все биты, кроме одного, равны 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)) # Теперь мы очень просто можем проверять, наступили или нет для сообщения определенные события: # 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 # Битовые операции сдвигов используются нечасто в программах на языках высокого уровня, тем не менее рассмотрим небольшой пример, поясняющий их работу: # In[12]: a = 0b0101 b = 0b1001 print('a << 1 = {:04b}'.format(a << 1)) print('b >> 2 = {:04b}'.format(b >> 2)) # Операция `a << 1` сдвинула все биты в двоичном представлении переменной `a` на одну позицию влево, при этом дописав справа один нулевой бит. Операция `b >> 2` сдвинула все биты переменной `b` вправо на две позиции, при этом младшие два бита `b` были отброшены, а слева дописаны два нулевых бита. # Если вы внимательно ознакомились с преобразованием чисел в двоичную систему счисления, то для вас будет очевидным тот факт, что операция сдвига переменной влево на $n$ позиций эквивалентна умножению ее же на $2^n$: # In[13]: a = 5 b = a << 3 c = a * (2 ** 3) b, c # Такой прием применяется опытными разработчиками в участках кода, где требуется максимальное быстродействие программы. Дело в том, что битовые операции наиболее быстро выполняются процессором, и выигрыш от использования сдвига влево вместо умножения может достигать десятки и сотни раз. # ## Операции сравнения # Язык программирования 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 # ## Логические операции # Логические операции пришли в языки программирования из таких областей науки, как дискретная математика, математическая логика и исчисление высказываний. Всего в 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) # В выражении `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 # Теперь давайте немного усложним пример предыдущий пример. Пусть нам теперь нужно определить, что `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 # Важной особенностью логических операций является то, что они вычисляются по так называемой **короткой схеме**: # 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 # Посмотрим, какой тип имеет результат логических операций в этом случае: # In[19]: type(a and b), type(a or b), type(b and c), type(d or b), type(a or a) # ## Строковые операции # Строковые операции принимают в качестве операндов строковый тип данных `str` и возвращают результат такого же типа. Для перечисленных ниже операций возможно использование комбинированных инструкций присваивания. # |
Название
| Синтаксис | Описание | # |------------------------------------|------------------------------------------|----------------------------------------------| # |
Конкатенация
|
x + y
|Возвращает строку, составленную из строки x и приписанной к ее концу строки y | # |
Дублирование
|
x * y
|Возвращает строку x, продублированную y раз | # Обратите внимание, что для представленных строковых операций используются знаки `+` и `*`, которые выше применялись для операций сложения и умножения чисел. Интерпретатор может понять, какую операцию выполнить, по тому, какой тип данных имеют ее операнды. Например, для чисел `+` означает сложение, а для строк - конкатенацию. Это не единственный пример, когда один и тот же знак используется для разных операций, как будет видно в других лекциях. При этом приоритет их остается тем же, что и для операций с числами (для строк это означает, что конкатенация имеет меньший приоритет, чем дублирование). # In[20]: s1 = 'hello, ' s2 = 'world' s3 = '!' s4 = 'result: ' s4 += s1 + s2 + s3 * 3 s4 # ### Получение срезов строк # **Срезом строки** (англ. *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* **не попадает** в результат (у интервала открытая правая граница)! # Символы в строке пронумерованы следующим образом (можно обращаться к ним как с помощью верхних положительных индексов, так и с помощью нижних отрицательных): # ![Нумерация символов](./images/05/symbols-numeration.png) # Простейшее использование операции среза заключается в получении одного символа из строки по его индексу: # In[21]: s = 'ABC DEF GHI' s[0], s[-11], s[5], s[-6] # Вторая форма записи операции взятия среза позволяет сразу получить целую подстроку: # In[22]: s[4:7], s[-7:-4] # При таком способе записи мы можем опустить любой из индексов: если не указать начальный индекс, то вместо него будет использован 0, если конечный, то вместо него будет использоваться индекс на 1 больше индекса последнего элемента (в нашем примере 10 + 1): # In[23]: s[:5], s[5:], s[:] # последний срез возвращает всю строку # Наконец, третья форма записи позволяет получить строку, которая заполняется символами оргинальной строки, взятыми с определенным шагом: # In[24]: # получаем символы от начала и до конца указанного интервала, начиная с первого и с шагом 4 (т.е. берем # первый символ, "шагаем" на 4 позиции вперед, берем следующий символ и так далее, пока не выйдем за # границы интервала или не достигнем его конца) s[0:7:4] # Если в качестве шага используется отрицательный индекс, то интерпретатор просматривает строку в обратном направлении: # In[25]: # получаем строку, состоящую из символов s[6], s[5] и s[4] s[6:3:-1] # В третьей форме записи также можно не указывать параметры *start* и *end*. В этом случае для положительного *step* строка просматривается с первого символа до конца строки, а для отрицательного - с последнего и до начала: # In[26]: s[::2], s[::-2] # Часто операция получения среза используется вместе с операцией конкатенации для изменения некоторого символа в строке: # 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 # ## Неявные преобразования типов # Несмотря на то, что язык программирования Python относится к языкам со [строгой типизацией](./04_Data_Types.ipynb#Определение-типа-данных), в нем существует несколько неявных преобразований типов данных, которые считаются безопасными и не приводящими к ошибкам в программах. Безопасными считаются такие преобразования, при которых не происходит потеря важной информации, например: # * новый тип данных позволяет хранить все значения, допустимые для старого, плюс возможно еще некоторые (говоря на языке теории множеств, все возможные значения старого типа данных являются подмножеством значений нового) # * та информация, которая теряется при преобразовании, не важна в данном контексте # Неявные преобразования типов используются интерпретатором в следующих ситуациях: # * В одном выражении могут быть смешаны константы и переменные разных типов. В этом случае интерпретатор пытается безопасно преобразовать значения к некоторому общему для всех типу. # * В некоторых операциях и инструкциях языка требуется константа или переменная определенного типа. Если в таком месте в программе встречается значение другого типа, то интерпретатор 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) # In[29]: a = 2 b = 4.6 + 8j c = b / a # a преобразуется в complex (int -> float -> complex) c, type(c) # Несколько более неожиданный результат, связанный со спецификой преобразования булевого типа, демонстрирует следующий пример: # 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) # В конце обратим ваше внимание на то, что в языке программирования Python отсутствует неявное преобразование из строки в число, поэтому выполнение такого кода завершается генерацией исключения `TypeError`: # In[31]: a = 10 s = '20' a + s # Несмотря на то, что для представленного выше примера неявное преобразование из строки в число скорее всего было бы тем, чего и хотел программист, зачастую оно может приводить к труднообнаруживаемым ошибкам, например, если в строке случайно окажется не цифровой символ, а буквенный. # ## Вопросы для самоконтроля # 1. Что такое операнд? # 2. На какие типы в зависимости от количества аргументов разделяются операции в Python? # 3. Перечислите все унарные операции, с которыми мы познакомились в этой лекции. # 4. Что такое приоритет операции? Зачем он нужен? # 5. В чем смысл короткой схемы вычисления логических операций? Какие именно сравнения будут выполнены интерпретатором в примере *In \[16\]* в случае, если `point` равно -1.0? # 6. Что такое срез строки? # 7. В каких случаях интерпретатор Python может использовать неявные преобразования типов? Когда их можно назвать безопасными? # ## Задание # 1. Напишите программу, находящую корни [квадратного уравнения](https://ru.wikipedia.org/wiki/%D0%9A%D0%B2%D0%B0%D0%B4%D1%80%D0%B0%D1%82%D0%BD%D0%BE%D0%B5_%D1%83%D1%80%D0%B0%D0%B2%D0%BD%D0%B5%D0%BD%D0%B8%D0%B5) $a*x^2+b*x+c=0$ для некоторых $a$, $b$ и $c$ (подсказка: $\sqrt{x}=x^\frac{1}{2}$). # 2. Используя [пример](#flags_example) про состояние сообщения, напишите программу, которая определяет, что сообщение или доставлено, или отредактировано, или и то и другое. Программа должна выводить True, если сообщение удовлетворяет перечисленным условиям, и False в противном случае. # 3. Напишите программу, которая выводит True, если некоторое слово одинаково выглядит как при написании слева направо, так и справа налево (например, "топот"), и False в противном случае. # - - - # [Предыдущая: Типы данных](04_Data_Types.ipynb) | # [Содержание](00_Overview.ipynb#Содержание) | # [Следующая: Инструкция ветвления и циклы](06_Branch_Instruction_And_Loops.ipynb)