Главная / Курсы / Python / Скалярные типы данных
# Глава 9. Скалярные типы данных К скалярным (простым, неделимым) встроенным типам данных относятся: числа (целые, дробные, комплексные), логические флаги (`True`, `False`) и тип `NoneType`, обозначающий «отсутствие значения». Рассмотрим каждый из них более подробно. ## Числа Занимательный факт: размер целых чисел в питоне не ограничен. Точнее, ограничен лишь объемом свободной оперативки. Теперь вы знаете, какой язык выбрать, если попадется олимпиадная задачка на большие числа. По умолчанию литералы целых чисел записываются в десятичной системе счисления: `month = 12`, `day = 31`. Чтобы явно задать другое основание, используются **префиксы:** - `0b` — двоичные числа: `0b1101`. - `0o` — восьмеричные числа: `0o732`. - `0x` — шестнадцатеричные числа: `0xAFF9`. Создайте переменную `hex_val`, которая соответствует значению 16 в десятеричной системе счисления, но записано в шестнадцатеричной системе счисления. {.task_text} ```python {.task_source #python_chapter_0090_task_0010} ``` Для представления числа в шестнадцатеричной системе не забудьте воспользоваться соответствующим префиксом. {.task_hint} ```python {.task_answer} hex_val = 0x10 ``` Наиболее популярные **операторы для работы с числами:** - `a = b` — присваивание. - `a + b` — сложение. - `a - b` — вычитание. - `a += 1` — инкремент. Вместо `1` может быть любое число. Упрощенного варианта инкремента (`a++`) и декремента (`a--`) в питоне **нет.** - `a -= 1` — декремент. - `a * b` — умножение. - `a / b` — деление. - `a // b` — целочисленное деление. - `a % b` — остаток от деления. - `a ** b`, `pow(a, b)` — возведение в степень. - `-a` — изменение знака. Запись `+a` тоже допустима, по сути ничего не делает, и применяется в редких случаях для улучшения восприятия формул. Помимо инкремента и декремента, существуют и другие комбинированные операторы присваивания. Например `a *= b`, `a /= b`. Наиболее популярные встроенные **функции для работы с числами:** - `abs(a)` — модуль числа. - `round(a, b)` — округление значения `a`. Если задан второй аргумент — то до заданного в нем количества знаков после запятой. Если аргумент не задан, то до ближайшего целого. - `divmod(a, b)` — возвращает два числа: частное и остаток от деления. - `bin(val)`, `oct(val)`, `hex(val)` — перевод числа в заданную систему счисления. - `int(s, base)` — конвертация строки в целое число в системе счисления с основанием `base` (если `base` не указан, подразумевается десятичная система). Если преобразование не удается, генерируется исключение `ValueError`. Напишите функцию `to_fahrenheit()`. Она должна принимать единственный аргумент: `celsius` градусов Цельсия и возвращать соответствующее значение по шкале Фаренгейта. {.task_text} Формула: °F = (°C × 9/5) + 32. {.task_text} ```python {.task_source #python_chapter_0090_task_0020} ``` Функция должна вернуть значение: `celsius * 9.0 / 5.0 + 32.0`. {.task_hint} ```python {.task_answer} def to_fahrenheit(celsius): return celsius * 9.0 / 5.0 + 32.0 ``` Как и в большинстве других языков, в питоне числа сравниваются между собой через операторы `>`, `>=`, `<`, `<=`, `==`, `!=`. Математический факт: комплексные числа, в отличие от вещественных, нельзя сравнивать между собой на «больше/меньше». Поэтому операторы `>`, `>=`, `<`, `<=` к комплексным числам (тип `complex`) **не применимы.** **Цепочки сравнений** Удобство использования математических операторов в питоне вышло на новый уровень благодаря цепочкам сравнений (comparison operator chaining). Они позволяют связывать множество сравнений в единую последовательность. Внутри нее условия неявно соединяются логическим `and`: ```python if 0 < val <= 12: print("...this value may be month") ``` Эта цепочка аналогична явному объединению с помощью `and`: ```python if 0 < val and val <= 12: print("...this value may be month") ``` Превратите условие внутри `if` в цепочку сравнений. {.task_text} ```python {.task_source #python_chapter_0090_task_0030} x = 1999 if x < 2001 and x > 1900: print("XX century!") ``` Вид цепочки сравнений: `min_val < x < max_val`. {.task_hint} ```python {.task_answer} x = 1999 if 1900 < x < 2001: print("XX century!") ``` Как видите, цепочки сравнений делают проверки более компактными и человекочитаемыми: ```python lower_bound <= a < b < c <= upper_bound ``` Но несмотря на мнимую простоту, цепочки сравнений — настоящая россыпь подводных камней. Будьте внимательны и не попадайтесь в ловушки цепочек сравнения. Ловушка **усложнение вместо упрощения** срабатывает, если цепочки сравнения запутывают код вместо того, чтобы упрощать. Например, когда операторы в условии идут не от меньшего большему в формате `a < b <= c < d`, а перемешаны: ```python if x < y > z: pass ``` Согласитесь, в данном случае отказ от цепочки сравнений в пользу встроенной функции `max()` делает код понятнее: ```python if max(x, z) < y: pass ``` Ловушка **использование в сравнении непостоянного выражения** захлопывается, если в середину цепочки закрадывается выражение с побочными эффектами либо на одинаковых данных возвращающее разный результат. Дело в том, что цепочки сравнений не только делают код короче, но и неявно оптимизируют его. Ожидаемо, что в этом примере функция `get_val()` вызовется дважды: ```python if a < get_val() and get_val() <= b: pass ``` Если же заменить его на цепочку сравнений, то `get_val()` будет вызван только один раз: ```python if a < get_val() <= b: pass ``` Помните это, если захотите переписать какое-то условие на цепочку сравнений. Ловушка **нетранзитивные операторы** грозит при проверке, что три переменные имеют разные значения. Обратная задача (проверить, что переменные одинаковы) действительно идеально укладывается в цепочку сравнений: ```python if x == y == z: pass ``` Но сработает ли проверка на неравенство? ```python if x != y != z: pass ``` Фактически она является заменой для `x != y and y != z`. Но это условие ничего не говорит о том, как соотносятся между собой `x` и `z`. Поэтому такая проверка работать не будет. А все потому, что с математической точки зрения равенство считается транзитивным отношением, а неравенство — нет. Перепишите эту функцию, чтобы она возвращала правильный результат: `True`, если все 3 переменные имеют разные значения (среди них нет одинаковых). Иначе `False`. {.task_text} ```python {.task_source #python_chapter_0090_task_0040} def not_eq(a, b, c): return a != b != c ``` Чтобы удостовериться, что все три переменные не равны между собой, их следует попарно сравнить: `a` и `b`, `a` и `c`, `b` и `c`. {.task_hint} ```python {.task_answer} def not_eq(a, b, c): return a != b and b != c and a != c ``` **Битовые операторы** К целым числам в питоне применимы битовые операторы: - `a & b` — битовый «И»: `1 & 4` равен 0, `1 & 5` - соответственно 1. - `a | b` — битовый «ИЛИ»: `1 | 4` дает 5. - `~a` — битовый «НЕ». Инверсия битов числа: ~5 равен отрицательному числу -6 из-за особенностей представления целых чисел в памяти. - `a ^ b` — битовый «исключающий ИЛИ» (XOR). `3 ^ 5` — это 6. Для битовых операторов существуют комбинированные операторы присваивания: `|=`, `&=` и так далее. Например, так выглядит извлечение из значения `val` 4 и 5 битов по маске: ```python val &= 0b110000 ``` Также в питоне реализованы операторы побитового сдвига: - `a << b` — сдвиг числа `a` влево на `b` битов. `5 << 1` — это 10. - `a >> b` — сдвиг вправо. `5 >> 1` — это 2. Напишите функцию `is_eq_abs(a, b, eps)`, проверяющую два числа с плавающей точкой `a` и `b` на равенство c точностью `eps` включительно. {.task_text} Например, `is_eq_abs(37.001, 37.002, 0.1)` вернет `True`, а `is_eq_abs(37.001, 37.002, 1e-5)` вернет `False`. {.task_text} ```python {.task_source #python_chapter_0090_task_0050} ``` Модуль разности чисел должен быть меньше или равен эпсилон. {.task_hint} ```python {.task_answer} def is_eq_abs(a, b, eps): return abs(a - b) <= eps ``` Что же касается чисел с плавающей точкой, то в модуле `math` содержится масса полезных функций для проведения математических вычислений. Но об этом позже. ## Логические значения В питоне есть два встроенных логических объекта-синглтона: `True` и `False`. Результат вычисления любого выражения в конечном итоге сводится к одному из них: ```python print(10 > 9) # True print(2 == 3) # False ``` Это относится и к блоку `if/else`. Пример проверки числа на четность: ```python if val % 2 == 0: print("val is even") else: print("val is odd") ``` К логическим значениям применимы операторы: - `and` — «И». В C++ и некоторых других языках этот оператор имеет вид `&&`. - `or` — «ИЛИ». В C++ это `||`. - `not` — «НЕ». Проверка, что дата удовлетворяет условиям, заданным некоей бизнес-логикой: ```python weekend = is_weekend(date) if day > 20 and not weekend: print("Good date for discounts!") ``` Напишите функцию `my_xor()`, которая принимает два флага и возвращает для них логический XOR. Например, `my_xor(True, False)` вернет `True`. {.task_text} ```python {.task_source #python_chapter_0090_task_0060} ``` XOR — это взаимоисключающее ИЛИ. Возвращает `True` только для различающихся аргументов. {.task_hint} ```python {.task_answer} def my_xor(a, b): return a != b ``` ## NoneType В питоне к типу `NoneType` принадлежит единственный на всю программу объект-синглтон `None`. По смыслу это очередное воплощение `null` из Java, C# и других языков. Обозначает отсутствие значения: ```python discount = None ``` Пример небольшой функции-обертки над вызовом `int()` для случаев, если на вызывающей стороне вместо исключений хочется обрабатывать проверку на `None`: ```python {.example_for_playground} def safe_get_int(x): try: return int(x) except ValueError: return None print(safe_get_int("20")) print(safe_get_int("here we get nothing")) ``` Для проверки на `None` используется ключевое слово `is`:{#block-compare} ```python if x is None: print("Got None for x") ``` ```python if db_conn is not None: select_table_rows(db_conn) ``` Почему для сравнения с `None` мы используем `is`, а не `==`? `is` — ключевое слово, проверяющее, являются ли два объекта одним и тем же объектом в памяти. `is` сравнивает не значения, а id объектов. В принципе сравнение с `None` через `==` тоже сработает. Но так делать не рекомендуется: сравнение через `==` вернет предсказуемый результат для всех встроенных типов, но для пользовательских классов этот оператор может быть переопределен. В результате сравнение с `None` вернет не то, что вы ожидали, и вас ждут часы увлекательной отладки. Второй аргумент в пользу `is` — он банально быстрее, чем `==`. Поэтому возьмите за правило сравниваться с `None` через `is`. Напишите функцию `left_shift(val, n)`, которая сдвигает `val` на `n` битов влево и возвращает результат. Но если `val` или `n` равны `None`, функция сразу возвращает `None`. {.task_text} ```python {.task_source #python_chapter_0090_task_0070} ``` Сдвиг влево реализуется как `val << n`. {.task_hint} ```python {.task_answer} def left_shift(val, n): if val is None or n is None: return None return val << n ``` # Резюмируем - Инкремент и декремент в питоне выглядят так: `i += 1`, `j -= 1`. - Если требуется сравнить несколько значений между собой, используйте цепочки сравнений: `2 < x < 6`. - В питоне нет ограничения на размер целого числа. - Сравнивать значение с `None` нужно через оператор `is`.
Отправка...
Наша группа в telegram. Здесь можно задавать вопросы и общаться.
Задонатить. Если вам нравится курс, вы можете поддержать развитие площадки!