Поговорим о странностях питона

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

Подпишись на группу Вконтакте и Телеграм-канал. Там еще больше полезного контента для программистов.
А на YouTube-канале ты найдешь обучающие видео по программированию. Подписывайся!

Что такое range, map, dict, tuple?

Первый пункт этой статьи связан с range(), map(), dict(), tuple(), которые очень часто называются функциями, хотя это не совсем так.

Дело в том, что, оказуется, это не функции. Воспользуемся функцией (!) type для определения типа объекта.

import sys

type(range), type(sum)
-----------------------------------------------
(type, builtin_function_or_method)

Типы range и sum не совпадают. Python утверждает, что range — это тип, а sum — встроенная функция или метод. Попробуем создать свою функцию и выяснить её тип.

def f() -> None:
    pass

type(f)
-----------------------------------------------
function

Ожидаемо, созданная пользователем функция имеет тип function. А теперь создадим свой класс и также воспользуемся type.

class C:
    pass

type(C)
-----------------------------------------------
type

Вот оно! Типы range и нашего класса совпадают. Получается, range — это класс, а используя передачу параметров, мы используем конструктор класса, который действительно создает объект range.

Проверим типы других известных «функций»:

type(tuple), type(list), type(map)
-----------------------------------------------
(type, type, type)

Получается, tuple, list и map это также классы, а передача параметров в круглых скобках — вызов конструктора класса. Если для tuple и list это утверждение более очевидно, то для map этот факт действительно удивительный.

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

Немного про изменяемость кортежей

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

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

t = (1, 2, 3, [4, 5])
t[3].append(6)
t
-----------------------------------------------
(1, 2, 3, [4, 5, 6])

И, конечно же, этот приём работает и для словарей.

t = ({1:2}, {3:4}, {})
t[2][5] = 6
t
-----------------------------------------------
({1:2}, {3:4}, {5:6})

Но такой кортеж не является хэшируемым, т. к. содержит в себе нехэшируемые объекты.

{t:1}
-----------------------------------------------
TypeError: unhashable type: 'dict'

И всё же, такая возможность изменять объекты внутри кортежа кажется странной.

Проблемы именованных параметров

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

def append_zero(l=[]):
    l.append(0)
    return l

l1 = append_zero()
print(l1)

l2 = append_zero()
print(l2)

l1 is l2
-----------------------------------------------
[0]
[0, 0]
True

Проблема на лицо. Эта проблема связана с ссылочной системой Python. Чтобы решить эту проблему, всего лишь немного поменяем функцию.

def append_zero(l=None):
    if l is None:
        l = list()

    l.append(0)
    return l

l1 = append_zero()
print(l1)

l2 = append_zero()
print(l2)

l1 is l2
-----------------------------------------------
[0]
[0]
True

Теперь всё работает ожидаемо. Приведу ещё один пример трудностей с ссылками.

a = [0, 1]
b = a

b.append(2)
a
-----------------------------------------------
[0, 1, 2]

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

Заключение

В этой статье мы обсудили некоторые странности и проблемы, которые могут возникнуть у новичка и не только при использовании языка программирования Python. Разумеется, это далеко не все странности, с которыми вы можете столкнуться. Советую попробовать обнаружить какую-то странность самостоятельно. Это довольно увлекательный процесс.

Советую прочитать статью «Бросок тела с высоты, моделируем на Python«.

А также подписывайтесь на группу ВКонтакте, Telegram и YouTube-канал. Там еще больше полезного и интересного для программистов.