#python #codegems
Для списков и других изменяемых последовательностей присваивание l2 = l1[:] также создает копию. Однако при использовании конструктора и оператора [:] создается поверхностная (shallow) копия (т. е. дублируется только самый внешний контейнер, который заполняется ссылками на те же элементы, что хранятся в исходном контейнере). Это экономит память и не создает проблем, если все элементы неизменяемые. Однако при наличии изменяемых элементов можно столкнуться с неприятными сюрпризами.
Слабые ссылки на объект не увеличивают счетчик ссылок. Таким образом, слабая ссылка не препятствует уничтожению объекта ссылки сборщиком мусора. Слабые ссылки полезны для кеширования, поскольку мы не хотим, чтобы кешированный объект оставался жив только потому, что на него ссылается сам кеш.
Для кортежа t конструкция t[:] не создает копию, а возвращает ссылку на сам объект. Ссылку на исходный кортеж мы получаем также, написав tuple(t). Такое же поведение свойственно экземплярам классов str, bytes и frozenset. frozenset – не последовательность, поэтому, когда fs является объектом frozenset, конструкция fs[:] не работает. Но fs.copy() дает точно такой же эффект: обманывает нас и возвращает ссылку на тот же объект, а вовсе не на его копию.
В некоторых ситуациях полезно иметь ссылку, которая сама по себе не удерживает объект «в мире живых». Примером может служить класс, желающий отслеживать все свои экземпляры. Это можно сделать с помощью слабых ссылок – низкоуровневого механизма, на базе которого построены более полезные коллекции WeakValueDictionary, WeakKeyDictionary, WeakSet и функция finalize – все из модуля weakref.
На самом деле тип объекта тоже можно изменить, просто присвоив другой класс его атрибуту class, но это неприкрытое зло.
Функцией высшего порядка называется функция, которая принимает функцию в качестве аргумента или возвращает в качестве значения. Примером может служить функция map. Другой пример – встроенная функция sorted: ее необязательный аргумент key позволяет задать функцию, которая применяется к каждому сортируемому элементу.
Если в классе определен метод call, то его экземпляры можно вызывать как функции. Обычно при вызове класса создается экземпляр именно этого класса, но такое поведение можно изменить, переопределив метод new.
Класс, в котором реализован метод call, – простой способ создать похожий на функцию объект, обладающий внутренним состоянием, которое должно сохраняться между вызовами. Хороший пример – декоратор. Декоратор должен быть вызываемым объектом, и иногда удобно иметь возможность «запоминать» что-то между вызовами декоратора (например, в случае кеширования результатов длительных вычислений для последующего использования) или разбить сложную реализацию на несколько методов.
Стремление к стопроцентному покрытию аннотациями типов означает, что вы применяете аннотирование без ясной цели, просто ради красивых показателей. К тому же это будет мешать команде добиться максимума от мощи и гибкости Python. Код без аннотаций типов должен без колебаний приниматься, если аннотации сделали бы API менее удобным для пользователя или без нужды усложнили бы реализацию.
Если факультативный параметр имеет изменяемый тип, то None – единственно разумное значение по умолчанию
К работе с утиной типизацией проще приступить и она обладает большей гибкостью, но возможность, что неподдерживаемые операции приведут к ошибке во время выполнения, остается. Номинальная типизация обнаруживает ошибки до начала выполнения, но иногда отвергает код, который работал бы правильно.
Для списков и других изменяемых последовательностей присваивание l2 = l1[:] также создает копию. Однако при использовании конструктора и оператора [:] создается поверхностная (shallow) копия (т. е. дублируется только самый внешний контейнер, который заполняется ссылками на те же элементы, что хранятся в исходном контейнере). Это экономит память и не создает проблем, если все элементы неизменяемые. Однако при наличии изменяемых элементов можно столкнуться с неприятными сюрпризами.
Слабые ссылки на объект не увеличивают счетчик ссылок. Таким образом, слабая ссылка не препятствует уничтожению объекта ссылки сборщиком мусора. Слабые ссылки полезны для кеширования, поскольку мы не хотим, чтобы кешированный объект оставался жив только потому, что на него ссылается сам кеш.
Для кортежа t конструкция t[:] не создает копию, а возвращает ссылку на сам объект. Ссылку на исходный кортеж мы получаем также, написав tuple(t). Такое же поведение свойственно экземплярам классов str, bytes и frozenset. frozenset – не последовательность, поэтому, когда fs является объектом frozenset, конструкция fs[:] не работает. Но fs.copy() дает точно такой же эффект: обманывает нас и возвращает ссылку на тот же объект, а вовсе не на его копию.
В некоторых ситуациях полезно иметь ссылку, которая сама по себе не удерживает объект «в мире живых». Примером может служить класс, желающий отслеживать все свои экземпляры. Это можно сделать с помощью слабых ссылок – низкоуровневого механизма, на базе которого построены более полезные коллекции WeakValueDictionary, WeakKeyDictionary, WeakSet и функция finalize – все из модуля weakref.
На самом деле тип объекта тоже можно изменить, просто присвоив другой класс его атрибуту class, но это неприкрытое зло.
Функцией высшего порядка называется функция, которая принимает функцию в качестве аргумента или возвращает в качестве значения. Примером может служить функция map. Другой пример – встроенная функция sorted: ее необязательный аргумент key позволяет задать функцию, которая применяется к каждому сортируемому элементу.
Если в классе определен метод call, то его экземпляры можно вызывать как функции. Обычно при вызове класса создается экземпляр именно этого класса, но такое поведение можно изменить, переопределив метод new.
Класс, в котором реализован метод call, – простой способ создать похожий на функцию объект, обладающий внутренним состоянием, которое должно сохраняться между вызовами. Хороший пример – декоратор. Декоратор должен быть вызываемым объектом, и иногда удобно иметь возможность «запоминать» что-то между вызовами декоратора (например, в случае кеширования результатов длительных вычислений для последующего использования) или разбить сложную реализацию на несколько методов.
Стремление к стопроцентному покрытию аннотациями типов означает, что вы применяете аннотирование без ясной цели, просто ради красивых показателей. К тому же это будет мешать команде добиться максимума от мощи и гибкости Python. Код без аннотаций типов должен без колебаний приниматься, если аннотации сделали бы API менее удобным для пользователя или без нужды усложнили бы реализацию.
Если факультативный параметр имеет изменяемый тип, то None – единственно разумное значение по умолчанию
К работе с утиной типизацией проще приступить и она обладает большей гибкостью, но возможность, что неподдерживаемые операции приведут к ошибке во время выполнения, остается. Номинальная типизация обнаруживает ошибки до начала выполнения, но иногда отвергает код, который работал бы правильно.
#python #codegems
Any – необычный тип, который располагается и в самом верху, и в самом низу иерархии типов. Это одновременно и самый общий тип (т. е. аргумент n: Any принимает значения любого типа), и самый специализированный, т. е. поддерживает любую операцию. По крайней мере, именно так средство проверки типов воспринимает Any. Разумеется, никакой тип не может поддерживать все возможные операции, поэтому использование Any лишает средство проверки типов возможности выполнить свою основную миссию: обнаруживать потенциально некорректные операции до того, как программа аварийно завершится с исключением во время выполнения.
Начиная с версии Python 3.10 мы можем писать str | bytes вместо Union[str, bytes]. Это короче и не нужно импортировать Optional и Union из модуля typing. Сравните старый и новый синтаксисы аннотаций типов для параметра plural функции show_count:
plural: Optional[str] = None # до
plural: str | None = None # после
Оператор | можно также применять в сочетании с isinstance и issubclass для построения второго аргумента: isinstance(x,int | str).
Есть функции, которые принимают аргументы типа str или bytes, но возвращают str, если аргумент имел тип str, и bytes, если он имел тип bytes. В таких случаях тип возвращаемого значения определяется типом входа, поэтому Union – не совсем подходящее решение. Чтобы правильно аннотировать подобные функции, нам нужна переменная-тип TypeVar.
Чтобы включить в аннотацию кортеж неопределенной длины, используемый как неизменяемый список, необходимо задать один тип, за которым следуют запятая и многоточие ...
Например, tuple[int, ...] – кортеж, состоящий из элементов типа int. Многоточие означает, что допустимо любое число элементов >= 1. Не существует способа задать поля разных типов в кортежах произвольной длины.
Аннотации stuff: tuple[Any, ...] и stuff: tuple означают одно и то же: stuff является кортежем неопределенной длины, содержащим объекты любого типа.
В общем случае в аннотациях типов параметров лучше использовать abc.Mapping или abc.MutableMapping, а не dict (или typing.Dict в унаследованном коде).
Идея специального типа TypeAlias в том, чтобы сделать создаваемые псевдонимы типов хорошо видимыми и упростить для них проверку типов. Начиная с версии Python 3.10 это рекомендуемый способ создания псевдонимов типов:
typing.Protocol дает нам статическую утиную типизацию.
Тип NoReturn - это специальный тип, использующийся только для аннотирования типов возвращаемых значений функций, которые вообще не возвращают управления. Обычно они существуют, чтобы возбуждать исключение. В стандартной библиотеке десятки таких функций. Например, функция sys.exit() возбуждает исключение SystemExit, чтобы завершить процесс Python.
Any – необычный тип, который располагается и в самом верху, и в самом низу иерархии типов. Это одновременно и самый общий тип (т. е. аргумент n: Any принимает значения любого типа), и самый специализированный, т. е. поддерживает любую операцию. По крайней мере, именно так средство проверки типов воспринимает Any. Разумеется, никакой тип не может поддерживать все возможные операции, поэтому использование Any лишает средство проверки типов возможности выполнить свою основную миссию: обнаруживать потенциально некорректные операции до того, как программа аварийно завершится с исключением во время выполнения.
Начиная с версии Python 3.10 мы можем писать str | bytes вместо Union[str, bytes]. Это короче и не нужно импортировать Optional и Union из модуля typing. Сравните старый и новый синтаксисы аннотаций типов для параметра plural функции show_count:
plural: Optional[str] = None # до
plural: str | None = None # после
Оператор | можно также применять в сочетании с isinstance и issubclass для построения второго аргумента: isinstance(x,int | str).
Есть функции, которые принимают аргументы типа str или bytes, но возвращают str, если аргумент имел тип str, и bytes, если он имел тип bytes. В таких случаях тип возвращаемого значения определяется типом входа, поэтому Union – не совсем подходящее решение. Чтобы правильно аннотировать подобные функции, нам нужна переменная-тип TypeVar.
Чтобы включить в аннотацию кортеж неопределенной длины, используемый как неизменяемый список, необходимо задать один тип, за которым следуют запятая и многоточие ...
Например, tuple[int, ...] – кортеж, состоящий из элементов типа int. Многоточие означает, что допустимо любое число элементов >= 1. Не существует способа задать поля разных типов в кортежах произвольной длины.
Аннотации stuff: tuple[Any, ...] и stuff: tuple означают одно и то же: stuff является кортежем неопределенной длины, содержащим объекты любого типа.
В общем случае в аннотациях типов параметров лучше использовать abc.Mapping или abc.MutableMapping, а не dict (или typing.Dict в унаследованном коде).
Идея специального типа TypeAlias в том, чтобы сделать создаваемые псевдонимы типов хорошо видимыми и упростить для них проверку типов. Начиная с версии Python 3.10 это рекомендуемый способ создания псевдонимов типов:
typing.Protocol дает нам статическую утиную типизацию.
Тип NoReturn - это специальный тип, использующийся только для аннотирования типов возвращаемых значений функций, которые вообще не возвращают управления. Обычно они существуют, чтобы возбуждать исключение. В стандартной библиотеке десятки таких функций. Например, функция sys.exit() возбуждает исключение SystemExit, чтобы завершить процесс Python.