1
0
mirror of https://github.com/satwikkansal/wtfpython synced 2024-06-08 14:17:48 +02:00

Fix typos and spelling

This commit is contained in:
Vadim Nifadev 2024-04-27 12:21:12 +03:00
parent 5bbdcdd5a5
commit d0dd3ad2b7

View File

@ -1,98 +1,163 @@
<p align="center"><img src="/images/logo.png#gh-light-mode-only" alt=""><img src="/images/logo-dark.png#gh-dark-mode-only" alt=""></p>
<h1 align="center">What the f*ck Python! 😱</h1>
<p align="center">Изучение и понимание Python с помощью нестандартного поведения и "магического" поведения.</p>
<p align="center">Изучение и понимание Python с помощью удивительных примеров поведения.</p>
Переводы: [English Original](https://github.com/satwikkansal/wtfpython) [Chinese 中文](https://github.com/robertparley/wtfpython-cn) | [Vietnamese Tiếng Việt](https://github.com/vuduclyunitn/wtfptyhon-vi) | [Spanish Español](https://web.archive.org/web/20220511161045/https://github.com/JoseDeFreitas/wtfpython-es) | [Korean 한국어](https://github.com/buttercrab/wtfpython-ko) | [Russian Русский](https://github.com/nifadyev/wtfpython/tree/main/translations/README-ru.md) | [German Deutsch](https://github.com/BenSt099/wtfpython) | [Add translation](https://github.com/satwikkansal/wtfpython/issues/new?title=Add%20translation%20for%20[LANGUAGE]&body=Expected%20time%20to%20finish:%20[X]%20weeks.%20I%27ll%20start%20working%20on%20it%20from%20[Y].)
Переводы: [English Original](https://github.com/satwikkansal/wtfpython) [Chinese 中文](https://github.com/robertparley/wtfpython-cn) | [Vietnamese Tiếng Việt](https://github.com/vuduclyunitn/wtfptyhon-vi) | [Spanish Español](https://web.archive.org/web/20220511161045/https://github.com/JoseDeFreitas/wtfpython-es) | [Korean 한국어](https://github.com/buttercrab/wtfpython-ko) | [Russian Русский](https://github.com/satwikkansal/wtfpython/tree/main/translations/README-ru.md) | [German Deutsch](https://github.com/BenSt099/wtfpython) | [Add translation](https://github.com/satwikkansal/wtfpython/issues/new?title=Add%20translation%20for%20[LANGUAGE]&body=Expected%20time%20to%20finish:%20[X]%20weeks.%20I%27ll%20start%20working%20on%20it%20from%20[Y].)
Альтернативные способы: [Интерактивный сайт](https://wtfpython-interactive.vercel.app) | [Интерактивный Jupiter notebook](https://colab.research.google.com/github/satwikkansal/wtfpython/blob/master/irrelevant/wtf.ipynb) | [CLI](https://pypi.python.org/pypi/wtfpython)
Python, будучи прекрасно спроектированным высокоуровневым языком программирования, предоставляет множество возможностей для удобства программиста. Но иногда результаты работы Python кода могут показаться неочевидными на первый взгляд.
Python, будучи прекрасно спроектированным высокоуровневым языком программирования, предоставляет множество возможностей для удобства программиста. Но иногда поведение Python кода могут показаться запутывающим на первый взгляд.
**wtfpython** задуман как проект, пытающийся объяснить, что именно происходит под капотом некоторых неочевидных фрагментов кода и менее известных возможностей Python.
**wtfpython** задуман как проект, пытающийся объяснить, что именно происходит под капотом неочевидных фрагментов кода и малоизвестных возможностей Python.
Если вы опытный программист на Python, вы можете принять это как вызов и правильно объяснить WTF ситуации с первой попытки. Возможно, вы уже сталкивались с некоторыми из них раньше, и я смогу оживить ваши старые добрые воспоминания! 😅
Если вы опытный питонист, вы можете принять это как вызов и правильно объяснить WTF ситуации с первой попытки. Возможно, вы уже сталкивались с некоторыми из них раньше, и я смогу оживить ваши старые добрые воспоминания! 😅
PS: Если вы уже читали **wtfpython** раньше, с изменениями можно ознакомиться [здесь](https://github.com/satwikkansal/wtfpython/releases/) (примеры, отмеченные звездочкой - это примеры, добавленные в последней основной редакции).
Ну что ж, приступим...
# Содержание
* [Содержание](#Содержание)
* [Структура примера](#Структура-примера)
* [Применение](#Применение)
* [👀 Примеры](#👀-Примеры)
* [Секция: Напряги мозги!](#Секция:-Напряги-мозги!)
* [▶ Первым делом!](#▶-Первым-делом!)
* [▶ Строки иногда ведут себя непредсказуемо](#▶-Строки-иногда-ведут-себя-непредсказуемо)
* [▶ Осторожнее с цепочкой операций](#▶-Осторожнее-с-цепочкой-операций)
* [▶ Как не надо использовать оператор `is`](#▶-Как-не-надо-использовать-оператор-`is`)
* [▶ Мистическое хэширование](#▶-Мистическое-хэширование)
* [В глубине души мы все одинаковы.](#▶-В-глубине-души-мы-все-одинаковы.)
* [▶ Беспорядок внутри порядка *](#▶-Беспорядок-внутри-порядка--*)
* [▶ Продолжай пытаться... *](#▶-Продолжай-пытаться...-*)
* [▶ Для чего?](#▶-Для-чего?)
* [▶ Расхождение во времени исполнения](#▶-Расхождение-во-времени-исполнения)
* [▶ `is not ...` не является `is (not ...)`](#▶-`is-not-...`-не-является-`is-(not-...)`)
* [▶ Крестики-нолики, где X побеждает с первой попытки!](#▶-Крестики-нолики,-где-X-побеждает-с-первой-попытки!)
* [▶ Переменная Шредингера *](#▶-Переменная-Шредингера-*)
* [▶ Проблема курицы и яйца *](#▶-Проблема-курицы-и-яйца-*)
* [▶ Отношения между подклассами](#▶-Отношения-между-подклассами)
* [▶ Равенство и тождество методов](#▶-Равенство-и-тождество-методов)
* [▶ All-true-ation (непереводимая игра слов) *](#▶-All-true-ation-(непереводимая-игра-слов)-*)
* [▶ Неожиданная запятая](#▶-Неожиданная-запятая)
* [▶ Строки и обратные слэши](#▶-Строки-и-обратные-слэши)
* [Не узел! (eng. not knot!)](#▶-Не-узел!-(eng.-not-knot!))
* [▶ Строки наполовину в тройных кавычках](#▶-Строки-наполовину-в-тройных-кавычках)
* [▶ Что не так с логическими значениями?](#▶-Что-не-так-с-логическими-значениями?)
* [▶ Атрибуты класса и экземпляра](#▶-Атрибуты-класса-и-экземпляра)
* [▶ Возврат None из генератора](#▶-Возврат-None-из-генератора)
* [▶ Yield from возвращает... *](#▶-Yield-from-возвращает...-*)
* [▶ Nan-рефлексивность *](#▶-Nan-рефлексивность-*)
* [▶ Мутируем немутируемое!](#▶-Мутируем-немутируемое!)
* [▶ Исчезающая переменная из внешней области видимости](#▶-Исчезающая-переменная-из-внешней-области-видимости)
* [▶ Превышение предела целочисленного преобразования строк](#▶-Превышение-предела-целочисленного-преобразования-строк)
* [Секция: Скользкие склоны](#Секция:-Скользкие-склоны)
* [▶ Изменение словаря во время прохода по нему](#▶-Изменение-словаря-во-время-прохода-по-нему)
* [▶ Упрямая операция `del`](#▶-Упрямая-операция-`del`)
* [▶ Переменная за пределами видимости](#▶-Переменная-за-пределами-видимости)
* [▶ Удаление элемента списка во время прохода по списку](#▶-Удаление-элемента-списка-во-время-прохода-по-списку)
* [▶ Сжатие итераторов с потерями *](#▶-Сжатие-итераторов-с-потерями-*)
* [▶ Утечка переменных внутри цикла](#▶-Утечка-переменных-внутри-цикла)
* [▶ Остерегайтесь изменяемых аргументов по умолчанию!](#▶-Остерегайтесь-изменяемых-аргументов-по-умолчанию!)
* [▶ Ловля исключений](#▶-Ловля-исключений)
* [▶ Одни и те же операнды, разная история!](#▶-Одни-и-те-же-операнды,-разная-история!)
* [▶ Разрешение имен игнорирует область видимости класса](#▶-Разрешение-имен-игнорирует-область-видимости-класса)
* [▶ Округляясь как банкир *](#▶-Округляясь-как-банкир-*)
* [▶ Иголки в стоге сена *](#▶-Иголки-в-стоге-сена-*)
* [▶ Сплиты (splitsies) *](#▶-Сплиты-(splitsies)-*)
* [▶ Подстановочное импортирование (wild imports) *](#▶-Подстановочное-импортирование-(wild-imports)-*)
* [▶ Все ли отсортировано? *](#▶-Все-ли-отсортировано?-*)
* [▶ Полночи не существует?](#▶-Полночи-не-существует?)
* [Секция: Скрытые сокровища!](#Секция:-Скрытые-сокровища!)
* [▶ Python, можешь ли ты помочь взлелеть?](#▶-Python,-можешь-ли-ты-помочь-взлелеть?)
* [▶ `goto`, но почему?](#▶-`goto`,-но-почему?)
* [▶ Держитесь!](#▶-Держитесь!)
* [▶ Давайте познакомимся с дружелюбным Дядей Барри](#▶-Давайте-познакомимся-с-дружелюбным-Дядей-Барри)
* [▶ Даже Python понимает, что любовь - это сложно.](#▶-Даже-Python-понимает,-что-любовь---это-сложно.)
* [▶ Да, оно существует!](#▶-Да,-оно-существует!)
* [▶ Многоточие *](#▶-Многоточие-*)
* [▶ Писконечность (Inpinity)](#▶-Писконечность-(Inpinity))
* [▶ Давайте искажать](#▶-Давайте-искажать)
* [Секция: Внешность обманчива!](#Секция:-Внешность-обманчива!)
* [▶ Пропускаем строки?](#▶-Пропускаем-строки?)
* [▶ Телепортация](#▶-Телепортация)
* [▶ Что-то не так...](#▶-Что-то-не-так...)
* [Секция: Разное](#Секция:-Разное)
* [▶ `+=` быстрее](#▶-`+=`-быстрее)
* [▶ Сделаем гигантскую строку!](#▶-Сделаем-гигантскую-строку!)
* [▶ Замедляем поиск по `dict` *](#▶-Замедляем-поиск-по-`dict`-*)
* [▶ Раздуваем экземпляры словарей *](#▶-Раздуваем-экземпляры-словарей-*)
* [▶ Минорное *](#▶-Минорное-*)
* [Вклад в проект](#Вклад-в-проект)
* [Благодарности](#Благодарности)
* [🎓 Лицензия](#🎓-Лицензия)
* [Удиви своих друзей!](#Удиви-своих-друзей!)
* [Нужна PDF версия?](#Нужна-PDF-версия?)
- [Содержание](#содержание)
- [Структура примера](#структура-примера)
- [Применение](#применение)
- [👀 Примеры](#-примеры)
- [Секция: Напряги мозги!](#секция-напряги-мозги)
- [▶ Первым делом!](#-первым-делом)
- [💡 Обьяснение](#-обьяснение)
- [▶ Строки иногда ведут себя непредсказуемо](#-строки-иногда-ведут-себя-непредсказуемо)
- [💡 Объяснение](#-объяснение)
- [▶ Осторожнее с цепочкой операций](#-осторожнее-с-цепочкой-операций)
- [💡 Объяснение:](#-объяснение-1)
- [▶ Как не надо использовать оператор `is`](#-как-не-надо-использовать-оператор-is)
- [💡 Объяснение:](#-объяснение-2)
- [▶ Мистическое хеширование](#-мистическое-хеширование)
- [💡 Объяснение](#-объяснение-3)
- [В глубине души мы все одинаковы.](#-в-глубине-души-мы-все-одинаковы)
- [💡 Объяснение:](#-объяснение-4)
- [▶ Беспорядок внутри порядка \*](#-беспорядок-внутри-порядка-)
- [💡 Объяснение:](#-объяснение-5)
- [▶ Продолжай пытаться... \*](#-продолжай-пытаться-)
- [💡 Объяснение:](#-объяснение-6)
- [▶ Для чего?](#-для-чего)
- [💡 Объяснение:](#-объяснение-7)
- [▶ Расхождение во времени исполнения](#-расхождение-во-времени-исполнения)
- [💡 Объяснение](#-объяснение-8)
- [▶ `is not ...` не является `is (not ...)`](#-is-not--не-является-is-not-)
- [💡 Объяснение](#-объяснение-9)
- [▶ Крестики-нолики, где X побеждает с первой попытки!](#-крестики-нолики-где-x-побеждает-с-первой-попытки)
- [💡 Объяснение:](#-объяснение-10)
- [▶ Переменная Шредингера \*](#-переменная-шредингера-)
- [💡 Объяснение:](#-объяснение-11)
- [▶ Проблема курицы и яйца \*](#-проблема-курицы-и-яйца-)
- [💡 Объяснение](#-объяснение-12)
- [▶ Отношения между подклассами](#-отношения-между-подклассами)
- [💡 Объяснение](#-объяснение-13)
- [▶ Равенство и тождество методов](#-равенство-и-тождество-методов)
- [💡 Объяснение](#-объяснение-14)
- [▶ All-true-ation (непереводимая игра слов) \*](#-all-true-ation-непереводимая-игра-слов-)
- [💡 Объяснение:](#-объяснение-15)
- [💡 Объяснение:](#-объяснение-16)
- [▶ Строки и обратные слэши](#-строки-и-обратные-слэши)
- [💡 Объяснение](#-объяснение-17)
- [Не узел! (англ. not knot!)](#-не-узел-англ-not-knot)
- [💡 Объяснение](#-объяснение-18)
- [▶ Строки, наполовину обернутые в тройные кавычки](#-строки-наполовину-обернутые-в-тройные-кавычки)
- [💡 Объяснение:](#-объяснение-19)
- [▶ Что не так с логическими значениями?](#-что-не-так-с-логическими-значениями)
- [💡 Объяснение:](#-объяснение-20)
- [▶ Атрибуты класса и экземпляра](#-атрибуты-класса-и-экземпляра)
- [💡 Объяснение:](#-объяснение-21)
- [▶ Возврат None из генератора](#-возврат-none-из-генератора)
- [💡 Объяснение:](#-объяснение-22)
- [▶ Yield from возвращает... \*](#-yield-from-возвращает-)
- [💡 Объяснение:](#-объяснение-23)
- [▶ Nan-рефлексивность \*](#-nan-рефлексивность-)
- [💡 Объяснение:](#-объяснение-24)
- [▶ Изменяем неизменяемое!](#-изменяем-неизменяемое)
- [💡 Объяснение:](#-объяснение-25)
- [▶ Исчезающая переменная из внешней области видимости](#-исчезающая-переменная-из-внешней-области-видимости)
- [💡 Объяснение:](#-объяснение-26)
- [▶ Загадочное преобразование типов ключей](#-загадочное-преобразование-типов-ключей)
- [💡 Объяснение:](#-объяснение-27)
- [▶ Посмотрим, сможете ли вы угадать что здесь?](#-посмотрим-сможете-ли-вы-угадать-что-здесь)
- [💡 Объяснение:](#-объяснение-28)
- [▶ Превышение предела целочисленного преобразования строк](#-превышение-предела-целочисленного-преобразования-строк)
- [💡 Объяснение:](#-объяснение-29)
- [Секция: Скользкие склоны](#секция-скользкие-склоны)
- [▶ Изменение словаря во время прохода по нему](#-изменение-словаря-во-время-прохода-по-нему)
- [💡 Объяснение:](#-объяснение-30)
- [▶ Упрямая операция `del`](#-упрямая-операция-del)
- [💡 Объяснение:](#-объяснение-31)
- [▶ Переменная за пределами видимости](#-переменная-за-пределами-видимости)
- [💡 Объяснение:](#-объяснение-32)
- [▶ Удаление элемента списка во время прохода по списку](#-удаление-элемента-списка-во-время-прохода-по-списку)
- [💡 Объяснение:](#-объяснение-33)
- [▶ Сжатие итераторов с потерями \*](#-сжатие-итераторов-с-потерями-)
- [💡 Объяснение:](#-объяснение-34)
- [▶ Утечка переменных внутри цикла](#-утечка-переменных-внутри-цикла)
- [💡 Объяснение:](#-объяснение-35)
- [▶ Остерегайтесь изменяемых аргументов по умолчанию!](#-остерегайтесь-изменяемых-аргументов-по-умолчанию)
- [💡 Объяснение:](#-объяснение-36)
- [▶ Ловля исключений](#-ловля-исключений)
- [💡 Объяснение](#-объяснение-37)
- [▶ Одни и те же операнды, разная история!](#-одни-и-те-же-операнды-разная-история)
- [💡 Объяснение:](#-объяснение-38)
- [▶ Разрешение имен игнорирует область видимости класса](#-разрешение-имен-игнорирует-область-видимости-класса)
- [💡 Объяснение](#-объяснение-39)
- [▶ Округляясь как банкир \*](#-округляясь-как-банкир-)
- [💡 Объяснение:](#-объяснение-40)
- [▶ Иголки в стоге сена \*](#-иголки-в-стоге-сена-)
- [💡 Объяснение:](#-объяснение-41)
- [▶ Сплиты (splitsies) \*](#-сплиты-splitsies-)
- [💡 Объяснение](#-объяснение-42)
- [▶ Подстановочное импортирование (wild imports) \*](#-подстановочное-импортирование-wild-imports-)
- [💡 Объяснение:](#-объяснение-43)
- [Все ли отсортировано? \*](#-все-ли-отсортировано-)
- [💡 Объяснение:](#-объяснение-44)
- [▶ Полночи не существует?](#-полночи-не-существует)
- [💡 Объяснение:](#-объяснение-45)
- [Секция: Скрытые сокровища!](#секция-скрытые-сокровища)
- [▶ Python, можешь ли ты помочь взлететь?](#-python-можешь-ли-ты-помочь-взлететь)
- [💡 Объяснение:](#-объяснение-46)
- [▶ `goto`, но почему?](#-goto-но-почему)
- [💡 Объяснение:](#-объяснение-47)
- [▶ Держитесь!](#-держитесь)
- [💡 Объяснение:](#-объяснение-48)
- [▶ Давайте познакомимся с дружелюбным Дядей Барри](#-давайте-познакомимся-с-дружелюбным-дядей-барри)
- [💡 Объяснение:](#-объяснение-49)
- [▶ Даже Python понимает, что любовь - это сложно.](#-даже-python-понимает-что-любовь---это-сложно)
- [💡 Объяснение:](#-объяснение-50)
- [▶ Да, оно существует!](#-да-оно-существует)
- [💡 Объяснение:](#-объяснение-51)
- [▶ Многоточие \*](#-многоточие-)
- [💡 Объяснение](#-объяснение-52)
- [▶ Писконечность (Inpinity)](#-писконечность-inpinity)
- [💡 Объяснение:](#-объяснение-53)
- [▶ Давайте искажать](#-давайте-искажать)
- [💡 Объяснение:](#-объяснение-54)
- [Секция: Внешность обманчива!](#секция-внешность-обманчива)
- [▶ Пропускаем строки?](#-пропускаем-строки)
- [💡 Объяснение](#-объяснение-55)
- [▶ Телепортация](#-телепортация)
- [💡 Объяснение:](#-объяснение-56)
- [▶ Что-то не так...](#-что-то-не-так)
- [💡 Объяснение](#-объяснение-57)
- [Секция: Разное](#секция-разное)
- [▶ `+=` быстрее `+`](#--быстрее-)
- [💡 Объяснение:](#-объяснение-58)
- [▶ Сделаем гигантскую строку!](#-сделаем-гигантскую-строку)
- [💡 Объяснение](#-объяснение-59)
- [▶ Замедляем поиск по `dict` \*](#-замедляем-поиск-по-dict-)
- [💡 Объяснение:](#-объяснение-60)
- [▶ Раздуваем экземпляры словарей \*](#-раздуваем-экземпляры-словарей-)
- [💡 Объяснение:](#-объяснение-61)
- [▶ Минорное \*](#-минорное-)
- [Вклад в проект](#вклад-в-проект)
- [Благодарности](#благодарности)
- [Несколько хороших ссылок!](#несколько-хороших-ссылок)
- [🎓 Лицензия](#-лицензия)
- [Удиви своих друзей!](#удиви-своих-друзей)
- [Нужна PDF версия?](#нужна-pdf-версия)
# Структура примера
@ -137,11 +202,11 @@ PS: Если вы уже читали **wtfpython** раньше, с измен
Хороший способ получить максимальную пользу от этих примеров - читать их последовательно, причем для каждого из них важно:
- Внимательно изучить исходный код. Если вы опытный программист на Python, то в большинстве случаев сможете предугадать, что произойдет дальше.
- Внимательно изучить исходный код. Если вы опытный Python программист, то в большинстве случаев сможете предугадать, что произойдет дальше.
- Прочитать фрагменты вывода и,
- Проверить, совпадают ли выходные данные с вашими ожиданиями.
- Убедиться, что вы знаете точную причину, по которой вывод получился именно таким.
- Если ответ отрицательный (что совершенно нормально), сделать глубокий вдох и прочитать объяснение (а если пример все еще непонятен, и создайте issue [здесь](https://github.com/satwikkansal/wtfpython/issues/new)).
- Если ответ отрицательный (что совершенно нормально), сделать глубокий вдох и прочитать объяснение (а если пример все еще непонятен, и создайте [issue](https://github.com/satwikkansal/wtfpython/issues/new)).
- Если "да", ощутите мощь своих познаний в Python и переходите к следующему примеру.
PS: Вы также можете читать WTFPython в командной строке, используя [pypi package](https://pypi.python.org/pypi/wtfpython),
@ -344,7 +409,7 @@ False
- Строки интернируются во время компиляции (`'wtf'` будет интернирована, но `''.join(['w'', 't', 'f'])` - нет)
- Строки, не состоящие из букв ASCII, цифр или знаков подчеркивания, не интернируются. В примере выше `'wtf!'` не интернируется из-за `!`. Реализацию этого правила в CPython можно найти [здесь](https://github.com/python/cpython/blob/3.6/Objects/codeobject.c#L19)
![image](/images/string-intern/string_intern.png)
- Когда переменные `a` и `b` принимают значение `"wtf!"` в одной строке, интерпретатор Python создает новый объект, а затем одновременно ссылается на вторую переменную. Если это выполняется в отдельных строках, он не "знает", что уже существует `"wtf!"` как объект (потому что `"wtf!"` не является неявно интернированным в соответствии с фактами, упомянутыми выше). Это оптимизация во время компиляции, не применяется к версиям CPython 3.7.x (более подробное обсуждение смотрите здесь [issue](https://github.com/satwikkansal/wtfpython/issues/100)).
- Когда переменные `a` и `b` принимают значение `"wtf!"` в одной строке, интерпретатор Python создает новый объект, а затем одновременно ссылается на вторую переменную. Если это выполняется в отдельных строках, он не "знает", что уже существует `"wtf!"` как объект (потому что `"wtf!"` не является неявно интернированным в соответствии с фактами, упомянутыми выше). Это оптимизация во время компиляции, не применяется к версиям CPython 3.7.x (более подробное обсуждение смотрите [здесь](https://github.com/satwikkansal/wtfpython/issues/100)).
- Единица компиляции в интерактивной среде IPython состоит из одного оператора, тогда как в случае модулей она состоит из всего модуля. `a, b = "wtf!", "wtf!"` - это одно утверждение, тогда как `a = "wtf!"; b = "wtf!"` - это два утверждения в одной строке. Это объясняет, почему тождества различны в `a = "wtf!"; b = "wtf!"`, но одинаковы при вызове в модуле.
- Резкое изменение в выводе четвертого фрагмента связано с [peephole optimization](https://en.wikipedia.org/wiki/Peephole_optimization) техникой, известной как складывание констант (англ. Constant folding). Это означает, что выражение `'a'*20` заменяется на `'aaaaaaaaaaaaaaaaaaaa'` во время компиляции, чтобы сэкономить несколько тактов во время выполнения. Складывание констант происходит только для строк длиной менее 21. (Почему? Представьте себе размер файла `.pyc`, созданного в результате выполнения выражения `'a'*10**10`). [Вот](https://github.com/python/cpython/blob/3.6/Python/peephole.c#L288) исходный текст реализации для этого.
- Примечание: В Python 3.7 складывание констант было перенесено из оптимизатора peephole в новый оптимизатор AST с некоторыми изменениями в логике, поэтому четвертый фрагмент не работает в Python 3.7. Подробнее об изменении можно прочитать [здесь](https://bugs.python.org/issue11549).
@ -378,7 +443,7 @@ False
#### 💡 Объяснение:
Согласно https://docs.python.org/3/reference/expressions.html#comparisons
Согласно [документации](https://docs.python.org/3/reference/expressions.html#comparisons)
> Формально, если a, b, c, ..., y, z - выражения, а op1, op2, ..., opN - операторы сравнения, то a op1 b op2 c ... y opN z эквивалентно a op1 b и b op2 c и ... y opN z, за исключением того, что каждое выражение оценивается не более одного раза.
@ -491,7 +556,7 @@ False
Подобная оптимизация применима и к другим **изменяемым** объектам, таким как пустые кортежи. Поскольку списки являются изменяемыми, поэтому `[] is []` вернет `False`, а `() is ()` вернет `True`. Это объясняет наш второй фрагмент. Перейдем к третьему,
**И `a`, и `b` ссылаются на один и тот же объект при инициализации одним и тем же значением в одной и той же строкеi**.
**И `a`, и `b` ссылаются на один и тот же объект при инициализации одним и тем же значением в одной и той же строке**.
**Вывод**
@ -511,7 +576,7 @@ False
* Когда a и b инициализируются со значением `257` в одной строке, интерпретатор Python создает новый объект, а затем одновременно ссылается на него во второй переменной. Если делать это в отдельных строках, интерпретатор не "знает", что объект `257` уже существует.
* Это оптимизация компилятора и относится именно к интерактивной среде. Когда вы вводите две строки в интерпретаторе, они компилируются отдельно, поэтому оптимизируются отдельно. Если выполнить этот пример в файле `.py', поведение будет отличаться, потому что файл компилируется весь сразу. Эта оптимизация не ограничивается целыми числами, она работает и для других неизменяемых типов данных, таких как строки (проверьте пример "Строки - это сложно") и плавающие числа,
* Эта оптимизация компилятора относится именно к интерактивной среде. Когда вы вводите две строки в интерпретаторе, они компилируются отдельно, поэтому оптимизируются отдельно. Если выполнить этот пример в файле `.py', поведение будет отличаться, потому что файл компилируется целиком. Эта оптимизация не ограничивается целыми числами, она работает и для других неизменяемых типов данных, таких как строки (смотреть пример "Строки - это сложно") и плавающие числа,
```py
>>> a, b = 257.0, 257.0
@ -519,12 +584,12 @@ False
True
```
* Почему это не сработало в Python 3.7? Абстрактная причина в том, что такие оптимизации компилятора зависят от реализации (т.е. могут меняться в зависимости от версии, ОС и т.д.). Я все еще выясняю, какое именно изменение реализации вызвало проблему, вы можете проверить этот [issue](https://github.com/satwikkansal/wtfpython/issues/100) для получения обновлений.
* Почему это не сработало в Python 3.7? Абстрактная причина в том, что такие оптимизации компилятора зависят от реализации (т.е. могут меняться в зависимости от версии, ОС и т.д.). Я все еще выясняю, какое именно изменение реализации вызвало проблему, вы можете следить за этим [issue](https://github.com/satwikkansal/wtfpython/issues/100) для получения обновлений.
---
### ▶ Мистическое хэширование
### ▶ Мистическое хеширование
<!-- Example ID: eb17db53-49fd-4b61-85d6-345c5ca213ff --->
1\.
```py
@ -556,7 +621,7 @@ complex
#### 💡 Объяснение
* Уникальность ключей в словаре Python определяется *эквивалентностью*, а не тождеством. Поэтому, даже если `5`, `5.0` и `5 + 0j` являются различными объектами разных типов, поскольку они равны, они не могут находиться в одном и том же `dict` (или `set`). Как только вы вставите любой из них, попытка поиска по любому другому, но эквивалентному ключу будет успешной с исходным сопоставленным значением (а не завершится ошибкой `KeyError`):
* Уникальность ключей в словаре Python определяется *эквивалентностью*, а не тождеством. Поэтому, даже если `5`, `5.0` и `5 + 0j` являются различными объектами разных типов, поскольку они эквивалентны, они не могут находиться в одном и том же `dict` (или `set`). Как только вы вставите любой из них, попытка поиска по любому другому, но эквивалентному ключу будет успешной с исходным сопоставленным значением (а не завершится ошибкой `KeyError`):
```py
>>> 5 == 5.0 == 5 + 0j
True
@ -569,7 +634,7 @@ complex
>>> (5 in some_dict) and (5 + 0j in some_dict)
True
```
* Это применимо и во время присваения значения элементу. Поэтому, в выражении `some_dict[5] = "Python"` Python находит существующий элемент с эквивалентным ключом `5.0 -> "Ruby"`, перезаписывает его значение на место, а исходный ключ оставляет в покое.
* Это применимо и во время присваивания значения элементу. Поэтому, в выражении `some_dict[5] = "Python"` Python находит существующий элемент с эквивалентным ключом `5.0 -> "Ruby"`, перезаписывает его значение на место, а исходный ключ оставляет в покое.
```py
>>> some_dict
{5.0: 'Ruby'}
@ -577,16 +642,17 @@ complex
>>> some_dict
{5.0: 'Python'}
```
* Итак, как мы можем обновить ключ до `5` (вместо `5.0`)? На самом деле мы не можем сделать это обновление на месте, но что мы можем сделать, так это сначала удалить ключ (`del some_dict[5.0]`), а затем установить его (`some_dict[5]`), чтобы получить целое число `5` в качестве ключа вместо плавающего `5.0`, хотя это нужно в редких случаях.
* Итак, как мы можем обновить ключ до `5` (вместо `5.0`)? На самом деле мы не можем сделать это обновление на месте, но все же это возможно, нужно сначала удалить ключ (`del some_dict[5.0]`), а затем установить его (`some_dict[5]`), чтобы получить целое число `5` в качестве ключа вместо плавающего `5.0`, хотя это нужно в редких случаях.
* Как Python нашел `5` в словаре, содержащем `5.0`? Python делает это за постоянное время без необходимости сканирования каждого элемента, используя хэш-функции. Когда Python ищет ключ `foo` в словаре, он сначала вычисляет `hash(foo)` (что выполняется в постоянном времени). Поскольку в Python требуется, чтобы объекты, которые одинаковы в сравнении, имели одинаковое хэш-значение (смотри [документацию](https://docs.python.org/3/reference/datamodel.html#object.__hash__)), `5`, `5.0` и `5 + 0j` выполняют это условие.
* Как Python нашел `5` в словаре, содержащем `5.0`? Python делает это за постоянное время без необходимости сканирования каждого элемента, используя хэш-функции. Когда Python ищет ключ `foo` в словаре, он сначала вычисляет `hash(foo)` (что выполняется в постоянном времени). Поскольку в Python требуется, чтобы объекты, которые сравниваются одинаково, имели одинаковое хэш-значение ([docs](https://docs.python.org/3/reference/datamodel.html#object.__hash__) здесь), `5`, `5.0` и `5 + 0j` имеют одинаковое хэш-значение.
```py
>>> 5 == 5.0 == 5 + 0j
True
>>> hash(5) == hash(5.0) == hash(5 + 0j)
True
```
**Примечание:** Обратное не обязательно верно: Объекты с одинаковыми хэш-значениями сами могут быть неравными. (Это вызывает так называемую [хэш-коллизию](https://en.wikipedia.org/wiki/Collision_(computer_science)) и ухудшает производительность постоянного времени, которую обычно обеспечивает хэширование).
**Примечание:** Обратное не обязательно верно: Объекты с одинаковыми хэш-значениями сами могут быть неравными. (Это вызывает так называемую [хэш-коллизию](https://en.wikipedia.org/wiki/Collision_(computer_science)) и ухудшает производительность постоянного времени, которую обычно обеспечивает хеширование).
---
@ -604,14 +670,14 @@ class WTF:
False
>>> WTF() is WTF() # идентификаторы также различаются
False
>>> hash(WTF()) == hash(WTF()) # хэши тоже должны отличаться
>>> hash(WTF()) == hash(WTF()) # хеши тоже должны отличаться
True
>>> id(WTF()) == id(WTF())
True
```
#### 💡 Объяснение:
* При вызове `id` Python создал объект класса `WTF` и передал его функции `id`. Функция `id` забирает свой `id` (местоположение в памяти) и выбрасывает объект. Объект уничтожается.
* При вызове `id` Python создал объект класса `WTF` и передал его функции `id`. Функция `id` забирает свой `id` (расположение в памяти) и выбрасывает объект. Объект уничтожается.
* Когда мы делаем это дважды подряд, Python выделяет ту же самую область памяти и для второго объекта. Поскольку (в CPython) `id` использует участок памяти в качестве идентификатора объекта, идентификатор двух объектов одинаков.
* Таким образом, id объекта уникален только во время жизни объекта. После уничтожения объекта или до его создания, другой объект может иметь такой же id.
* Но почему выражение с оператором `is` равно `False`? Давайте посмотрим с помощью этого фрагмента.
@ -641,7 +707,7 @@ True
---
### ▶ Беспорядок внутри порядка *
### ▶ Беспорядок внутри порядка *
<!-- Example ID: 91bff1f8-541d-455a-9de4-6cd8ff00ea66 --->
```py
from collections import OrderedDict
@ -657,13 +723,13 @@ another_ordered_dict[2] = 'b'; another_ordered_dict[1] = 'a';
class DictWithHash(dict):
"""
A dict that also implements __hash__ magic.
Словарь с реализованным методом __hash__.
"""
__hash__ = lambda self: 0
class OrderedDictWithHash(OrderedDict):
"""
An OrderedDict that also implements __hash__ magic.
OrderedDict с реализованным методом __hash__.
"""
__hash__ = lambda self: 0
```
@ -677,7 +743,7 @@ True
>>> ordered_dict == another_ordered_dict # почему же c != a ??
False
# Мы все знаем, что множество состоит только из уникальных элементов,
# Мы все знаем, что множество состоит из уникальных элементов,
# давайте попробуем составить множество из этих словарей и посмотрим, что получится...
>>> len({dictionary, ordered_dict, another_ordered_dict})
@ -695,7 +761,7 @@ TypeError: unhashable type: 'dict'
>>> another_ordered_dict[2] = 'b'; another_ordered_dict[1] = 'a';
>>> len({dictionary, ordered_dict, another_ordered_dict})
1
>>> len({ordered_dict, another_ordered_dict, dictionary}) # changing the order
>>> len({ordered_dict, another_ordered_dict, dictionary}) # изменим порядок элементов
2
```
@ -703,7 +769,7 @@ TypeError: unhashable type: 'dict'
#### 💡 Объяснение:
- Переходное (интрантизивное) равенство между `dictionary`, `ordered_dict` и `another_ordered_dict` не выполняется из-за реализации магического метода `__eq__` в классе `OrderedDict`. Перевод цитаты из [документации](https://docs.python.org/3/library/collections.html#ordereddict-objects)
- Переходное (интранзитивное) равенство между `dictionary`, `ordered_dict` и `another_ordered_dict` не выполняется из-за реализации магического метода `__eq__` в классе `OrderedDict`. Перевод цитаты из [документации](https://docs.python.org/3/library/collections.html#ordereddict-objects)
> Тесты равенства между объектами OrderedDict чувствительны к порядку и реализуются как `list(od1.items())==list(od2.items())`. Тесты на равенство между объектами `OrderedDict` и другими объектами Mapping нечувствительны к порядку, как обычные словари.
- Причина такого поведения равенства в том, что оно позволяет напрямую подставлять объекты `OrderedDict` везде, где используется обычный словарь.
@ -794,7 +860,7 @@ Iteration 0
#### 💡 Объяснение:
- Когда один из операторов `return`, `break` или `continue` выполняется в блоке `try` оператора "try...finally", на выходе также выполняется пункт `finally`.
- Когда один из операторов `return`, `break` или `continue` выполняется в блоке `try` оператора "try...finally", на выходе также выполняется блок `finally`.
- Возвращаемое значение функции определяется последним выполненным оператором `return`. Поскольку блок `finally` выполняется всегда, оператор `return`, выполненный в блоке `finally`, всегда будет последним.
- Предостережение - если в блоке `finally` выполняется оператор `return` или `break`, то временно сохраненное исключение отбрасывается.
@ -966,7 +1032,7 @@ board = [row] * 3
[['X', '', ''], ['X', '', ''], ['X', '', '']]
```
Мы же не назначили три `"Х"`?
Мы же не назначали три `"Х"`?
#### 💡 Объяснение:
@ -1006,7 +1072,7 @@ for x in range(7):
funcs_results = [func() for func in funcs]
```
**Вывод (Python version):**
**Вывод:**
```py
>>> results
[0, 1, 2, 3, 4, 5, 6]
@ -1039,7 +1105,7 @@ ClosureVars(nonlocals={}, globals={'x': 6}, builtins={}, unbound=set())
[42, 42, 42, 42, 42, 42, 42]
```
* Чтобы получить желаемое поведение, вы можете передать переменную цикла как именованную переменную в функцию. **Почему это работает?** Потому что это определит переменную *внутри* области видимости функции. Она больше не будет обращаться к глобальной области видимости для поиска значения переменной, а создаст локальную переменную, которая будет хранить значение `x` в данный момент времени.
* Чтобы получить желаемое поведение, вы можете передать переменную цикла как именованную аргумент в функцию. **Почему это работает?** Потому что это определит переменную *внутри* области видимости функции. Она больше не будет обращаться к глобальной области видимости для поиска значения переменной, а создаст локальную переменную, которая будет хранить значение `x` в данный момент времени.
```py
funcs = []
@ -1079,7 +1145,7 @@ True
True
```
Так какой же базовый класс является "окончательным"? Кстати, это еще не все,
Так какой же базовый класс является "родительским"? Кстати, это еще не все,
2\.
@ -1137,12 +1203,11 @@ False
* Отношения подклассов не обязательно являются транзитивными в Python. Можно переопределить магический метод `__subclasscheck__` в метаклассе.
* Когда вызывается `issubclass(cls, Hashable)`, он просто ищет не-фальшивый метод "`__hash__`" в `cls` или во всем, от чего он наследуется.
* Поскольку `object` является хэшируемым, а `list` - нехэшируемым, это нарушает отношение транзитивности.
* Более подробное объяснение можно найти [здесь] (https://www.naftaliharris.com/blog/python-subclass-intransitivity/).
* Поскольку `object` является хэшируемым, а `list` - нет, это нарушает отношение транзитивности.
* Более подробное объяснение можно найти [здесь](https://www.naftaliharris.com/blog/python-subclass-intransitivity/).
---
### ▶ Равенство и тождество методов
<!-- Example ID: 94802911-48fe-4242-defa-728ae893fa32 --->
@ -1202,7 +1267,7 @@ True
#### 💡 Объяснение
* Функции являются [дескрипторами](https://docs.python.org/3/howto/descriptor.html). Всякий раз, когда к функции обращаются как к
атрибута, вызывается дескриптор, создавая объект метода, который "связывает" функцию с объектом, владеющим атрибутом. При вызове метод вызывает функцию, неявно передавая связанный объект в качестве первого аргумента
атрибуту, вызывается дескриптор, создавая объект метода, который "связывает" функцию с объектом, владеющим атрибутом. При вызове метод вызывает функцию, неявно передавая связанный объект в качестве первого аргумента
(именно так мы получаем `self` в качестве первого аргумента, несмотря на то, что не передаем его явно).
```py
>>> o1.method
@ -1338,7 +1403,7 @@ True
#### 💡 Объяснение
- В обычной строке обратная слэш используется для экранирования символов, которые могут иметь специальное значение (например, одинарная кавычка, двойная кавычка и сам обратный слэш).
- В обычной строке обратный слэш используется для экранирования символов, которые могут иметь специальное значение (например, одинарная кавычка, двойная кавычка и сам обратный слэш).
```py
>>> "wt\"f"
'wt"f'
@ -1355,12 +1420,12 @@ True
>>> print(r"\\n")
'\\n'
```
- Это означает, что когда синтаксический анализатор встречает обратный слэш в необработанной строке, он ожидает, что за ней последует другой символ. А в нашем случае (`print(r"\")`) обратная слэш экранирует двойную кавычку, оставив парсер без завершающей кавычки (отсюда `SyntaxError`). Вот почему обратный слеш не работает в конце необработанной строки.
- Это означает, что когда синтаксический анализатор встречает обратный слэш в необработанной строке, он ожидает, что за ней последует другой символ. А в нашем случае (`print(r"\")`) обратный слэш экранирует двойную кавычку, оставив синтаксический анализатор без завершающей кавычки (отсюда `SyntaxError`). Вот почему обратный слэш не работает в конце необработанной строки.
--
---
### ▶ Не узел! (eng. not knot!)
### ▶ Не узел! (англ. not knot!)
<!-- Example ID: 7034deb1-7443-417d-94ee-29a800524de8 --->
```py
x = True
@ -1383,12 +1448,12 @@ SyntaxError: invalid syntax
* Старшинство операторов влияет на выполнение выражения, и оператор `==` имеет более высокий приоритет, чем оператор `not` в Python.
* Поэтому `not x == y` эквивалентно `not (x == y)`, что эквивалентно `not (True == False)`, в итоге равное `True`.
* Но `x == not y` вызывает `SyntaxError`, потому что его можно считать эквивалентным `(x == not) y`, а не `x == (not y)`, что можно было бы ожидать на первый взгляд.
* Парсер ожидал, что ключевое слово `not` будет частью оператора `not in` (потому что оба оператора `==` и `not in` имеют одинаковый приоритет), но после того, как он не смог найти ключевое слово `in`, следующее за `not`, он выдает `SyntaxError`.
* Синтаксический анализатор (англ. parser) ожидал, что ключевое слово `not` будет частью оператора `not in` (потому что оба оператора `==` и `not in` имеют одинаковый приоритет), но после того, как он не смог найти ключевое слово `in`, следующее за `not`, он выдает `SyntaxError`.
---
### ▶ Строки наполовину в тройных кавычках
### ▶ Строки, наполовину обернутые в тройные кавычки
<!-- Example ID: c55da3e2-1034-43b9-abeb-a7a970a2ad9e --->
**Вывод:**
```py
@ -1503,7 +1568,7 @@ I have lost faith in truth!
* Изначально в Python не было типа `bool` (использовали 0 для false и ненулевое значение 1 для true). В версиях 2.x были добавлены `True`, `False` и тип `bool`, но для обратной совместимости `True` и `False` нельзя было сделать константами. Они просто были встроенными переменными, и их можно было переназначить.
* Python 3 был несовместим с предыдущими версиями, эту проблему наконец-то исправили, и поэтому последний фрагмент не будет работать с Python 3.x!
* Python 3 несовместим с предыдущими версиями, эту проблему наконец-то исправили, и поэтому последний фрагмент не будет работать с Python 3.x!
---
@ -1651,7 +1716,7 @@ def some_func(x):
[]
```
То же самое, это тоже не сработало. Что происходит?
Опять не сработало. Что происходит?
#### 💡 Объяснение:
@ -1659,7 +1724,7 @@ def some_func(x):
> "... `return expr` в генераторе вызывает исключение `StopIteration(expr)` при выходе из генератора."
+ В случае `some_func(3)` `StopIteration` возникает в начале из-за оператора `return`. Исключение `StopIteration` автоматически перехватывается внутри обертки `list(...)` и цикла `for`. Поэтому два вышеприведенных фрагмента приводят к пустому списку.
+ В случае `some_func(3)` `StopIteration` возникает в начале из-за оператора `return`. Исключение `StopIteration` автоматически перехватывается внутри обертки `list(...)` и цикла `for`. Поэтому два вышеприведенных фрагмента возвращают пустой список.
+ Чтобы получить `["wtf"]` из генератора `some_func`, нужно перехватить исключение `StopIteration`.
@ -1721,7 +1786,7 @@ nan
```py
>>> x = float('nan')
>>> y = x / x
>>> y is y # идендичность сохраняется
>>> y is y # идентичность сохраняется
True
>>> y == y # сравнение ложно для y
False
@ -1733,7 +1798,7 @@ True
- `'inf'` и `'nan'` - это специальные строки (без учета регистра), которые при явном приведении к типу `float` используются для представления математической "бесконечности" и "не число" соответственно.
- Согласно стандартам IEEE `NaN != NaN`, но соблюдение этого правила нарушает предположение о рефлексивности элемента коллекции в Python, то есть если `x` является частью коллекции, такой как `list`, реализации, методы сравнения предполагают, что `x == x`. Поэтому при сравнении элементов сначала сравниваются их идентификаторы (так как это быстрее), а значения сравниваются только при несовпадении идентификаторов. Следующий фрагмент сделает вещи более ясными:
- Согласно стандартам IEEE, `NaN != NaN`, но соблюдение этого правила нарушает предположение о рефлексивности элемента коллекции в Python, то есть если `x` является частью коллекции, такой как `list`, реализации методов сравнения предполагают, что `x == x`. Поэтому при сравнении элементов сначала сравниваются их идентификаторы (так как это быстрее), а значения сравниваются только при несовпадении идентификаторов. Следующий фрагмент сделает вещи более ясными:
```py
>>> x = float('nan')
@ -1753,7 +1818,7 @@ True
---
### ▶ Мутируем немутируемое!
### ▶ Изменяем неизменяемое!
<!-- Example ID: 15a9e782-1695-43ea-817a-a9208f6bb33d --->
@ -1783,8 +1848,7 @@ TypeError: 'tuple' object does not support item assignment
* Перевод цитаты из [документации](https://docs.python.org/3/reference/datamodel.html)
> Неизменяемые последовательности
Объект неизменяемого типа последовательности не может измениться после создания. (Если объект содержит ссылки на другие объекты, эти объекты могут быть изменяемыми и могут быть изменены; однако набор объектов, на которые непосредственно ссылается неизменяемый объект, не может изменяться.)
> Объект неизменяемого типа последовательности не может измениться после создания. (Если объект содержит ссылки на другие объекты, эти объекты могут быть изменяемыми и могут быть изменены; однако набор объектов, на которые непосредственно ссылается неизменяемый объект, не может изменяться.)
* Оператор `+=` изменяет список на месте. Присваивание элемента не работает, но когда возникает исключение, элемент уже был изменен на месте.
* Также есть объяснение в официальном [Python FAQ](https://docs.python.org/3/faq/programming.html#why-does-a-tuple-i-item-raise-an-exception-when-the-addition-works).
@ -1836,30 +1900,30 @@ NameError: name 'e' is not defined
del N
```
Это означает, что исключению должно быть присвоено другое имя, чтобы на него можно было ссылаться после завершения блока `except`. Исключения очищаются, потому что с прикрепленным к ним трейсбэком они образуют цикл ссылок со стеком вызовов, сохраняя все локальные объекты в этой стэке до следующей сборки мусора.
Это означает, что исключению должно быть присвоено другое имя, чтобы на него можно было ссылаться после завершения блока `except`. Исключения очищаются, потому что с прикрепленным к ним трейсбэком они образуют цикл ссылок со стэком вызовов, сохраняя все локальные объекты в этой стэке до следующей сборки мусора.
* В Python clauses не имеют области видимости. В примере все объекты в одной области видимости, а переменная `e` была удалена из-за выполнения блока `except`. Этого нельзя сказать о функциях, которые имеют отдельные внутренние области видимости. Пример ниже иллюстрирует это:
```py
def f(x):
del(x)
print(x)
```py
def f(x):
del(x)
print(x)
x = 5
y = [5, 4, 3]
```
x = 5
y = [5, 4, 3]
```
**Результат:**
```py
>>> f(x)
UnboundLocalError: local variable 'x' referenced before assignment
>>> f(y)
UnboundLocalError: local variable 'x' referenced before assignment
>>> x
5
>>> y
[5, 4, 3]
```
**Результат:**
```py
>>> f(x)
UnboundLocalError: local variable 'x' referenced before assignment
>>> f(y)
UnboundLocalError: local variable 'x' referenced before assignment
>>> x
5
>>> y
[5, 4, 3]
```
* В Python 2.x, имя переменной `e` назначается на экземпляр `Exception()`, и при попытки вывести значение `e` ничего не выводится.
@ -1954,13 +2018,9 @@ a, b = a[b] = {}, 5
> Оператор присваивания исполняет список выражений (помните, что это может быть одно выражение или список, разделенный запятыми, в последнем случае получается кортеж) и присваивает единственный результирующий объект каждому из целевых списков, слева направо.
* `+` в `(target_list "=")+` означает, что может быть **один или более** целевых списков. В данном случае целевыми списками являются `a, b` и `a[b]` (обратите внимание, что список выражений ровно один, в нашем случае это `{}, 5`).
* После исполнения списка выражений его значение распаковывается в целевые списки **слева направо**. Так, в нашем случае сначала кортеж `{}, 5` распаковывается в `a, b`, и теперь у нас есть `a = {}` и `b = 5`.
* Теперь `a` имеет значение `{}`, которое является изменяемым объектом.
* Вторым целевым списком является `a[b]` (вы можете ожидать, что это вызовет ошибку, поскольку `a` и `b` не были определены в предыдущих утверждениях. Но помните, мы только что присвоили `a` значение `{}` и `b` - `5`).
* Теперь мы устанавливаем ключ `5` в словаре в кортеж `({}, 5)`, создавая круговую ссылку (`{...}` в выводе ссылается на тот же объект, на который уже ссылается `a`). Другим более простым примером круговой ссылки может быть
```py
@ -2010,7 +2070,7 @@ ValueError: Exceeds the limit (4300) for integer string conversion:
#### 💡 Объяснение:
Этот вызов `int()` прекрасно работает в Python 3.10.6 и вызывает ошибку `ValueError` в Python 3.10.8, 3.11. Обратите внимание, что Python все еще может работать с большими целыми числами. Ошибка возникает только при преобразовании между целыми числами и строками.
К счастью, вы можете увеличить предел допустимого количества цифр. Для этого можно воспользоваться одним из следующих способов:
- `-X int_max_str_digits` - флаг командной строкиcommand-line flag
- `-X int_max_str_digits` - флаг командной строки
- `set_int_max_str_digits()` - функция из модуля `sys`
- `PYTHONINTMAXSTRDIGITS` - переменная окружения
@ -2230,7 +2290,7 @@ for idx, item in enumerate(list_4):
>>> some_list = [1, 2, 3, 4]
>>> id(some_list)
139798789457608
>>> id(some_list[:]) # Notice that python creates new object for sliced list.
>>> id(some_list[:]) # Обратите внимание, создается новый объект из среза списка
139798779601192
```
@ -2239,7 +2299,7 @@ for idx, item in enumerate(list_4):
* `remove` удаляет первое подходящее значение, а не конкретный индекс, вызывает `ValueError`, если значение не найдено.
* `pop` удаляет элемент по определенному индексу и возвращает его, вызывает `IndexError`, если указан неверный индекс.
**Почему на выходе получается `[2, 4]`?
**Почему на выходе получается `[2, 4]`?**
- Проход по списку выполняется индекс за индексом, и когда мы удаляем `1` из `list_2` или `list_4`, содержимое списков становится `[2, 3, 4]`. Оставшиеся элементы сдвинуты вниз, то есть `2` находится на индексе 0, а `3` - на индексе 1. Поскольку на следующей итерации будет просматриваться индекс 1 (который и есть `3`), `2` будет пропущен полностью. Аналогичное произойдет с каждым альтернативным элементом в последовательности списка.
* Объяснение примера можно найти на [StackOverflow](https://stackoverflow.com/questions/45946228/what-happens-when-you-try-to-delete-a-list-element-while-iterating-over-it).
@ -2354,7 +2414,7 @@ print(x, ': x in global')
#### 💡 Объяснение:
- В Python циклы for используют область видимости, в которой они существуют, и оставляют свою определенную переменную цикла после завершения. Это также относится к случаям, когда мы явно определили переменную цикла for в глобальном пространстве имен. В этом случае будет произведена перепривязка существующей переменной.
- В Python циклы for используют область видимости, в которой они существуют, и оставляют свою определенную переменную цикла после завершения. Это также относится к случаям, когда мы явно определили переменную цикла for в глобальном пространстве имен. В этом случае будет произведена повторная привязка существующей переменной.
- Различия в выводе интерпретаторов Python 2.x и Python 3.x для примера с пониманием списков можно объяснить следующим изменением, задокументированным в журнале изменений [What's New In Python 3.0](https://docs.python.org/3/whatsnew/3.0.html):
@ -2386,7 +2446,7 @@ def some_func(default_arg=[]):
#### 💡 Объяснение:
- Изменяемые аргументы функций по умолчанию в Python на самом деле не инициализируются каждый раз, когда вы вызываете функцию. Вместо этого в качестве значения по умолчанию используется недавно присвоенное им значение. Когда мы явно передали `[]` в `some_func в качестве аргумента, значение по умолчанию переменной `default_arg` не было использовано, поэтому функция вернулась, как и ожидалось.
- Изменяемые аргументы функций по умолчанию в Python на самом деле не инициализируются каждый раз, когда вы вызываете функцию. Вместо этого в качестве значения по умолчанию используется недавно присвоенное им значение. Когда мы явно передали `[]` в `some_func` в качестве аргумента, значение по умолчанию переменной `default_arg` не было использовано, поэтому функция вернулась, как и ожидалось.
```py
def some_func(default_arg=[]):
@ -2396,7 +2456,7 @@ def some_func(default_arg=[]):
**Результат:**
```py
>>> some_func.__defaults__ # Выражение выведет значения стандартных аргументов фукнции
>>> some_func.__defaults__ # Выражение выведет значения стандартных аргументов функции
([],)
>>> some_func()
>>> some_func.__defaults__
@ -2580,7 +2640,7 @@ class SomeClass:
#### 💡 Объяснение
- Области видимости, вложенные внутрь определения класса, игнорируют имена, связанные на уровне класса.
- Выражение-генератор имеет свою собственную область видимости.
- Начиная с версии Python 3.X, списковые вычисления также имеют свою собственную область видимости.
- Начиная с версии Python 3.X, списковые выражения (list comprehensions) также имеют свою собственную область видимости.
---
@ -2716,7 +2776,7 @@ b = "javascript"
**Результат:**
```py
# assert выражение с сообщением об ошиб
# assert выражение с сообщением об ошибке
>>> assert(a == b, "Both languages are different")
# Исключение AssertionError не возникло
```
@ -2939,7 +2999,7 @@ False
(tuple, list)
```
- В отличие от метода `sorted, метод `reversed` возвращает итератор. Почему? Потому что сортировка требует, чтобы итератор либо изменялся на месте, либо использовал дополнительный контейнер (список), в то время как реверсирование может работать просто путем итерации от последнего индекса к первому.
- В отличие от метода `sorted`, метод `reversed` возвращает итератор. Почему? Потому что сортировка требует, чтобы итератор либо изменялся на месте, либо использовал дополнительный контейнер (список), в то время как реверсирование может работать просто путем итерации от последнего индекса к первому.
- Поэтому при сравнении `sorted(y) == sorted(y)` первый вызов `sorted()` будет потреблять итератор `y`, а следующий вызов просто вернет пустой список.
@ -2992,7 +3052,7 @@ if noon_time:
Секция содержит менее известные интересные нюансы работы Python, которые неизвестны большинству новичков.
### ▶ Python, можешь ли ты помочь взлелеть?
### ▶ Python, можешь ли ты помочь взлететь?
<!-- Example ID: a92f3645-1899-4d50-9721-0031be4aec3f --->
Что ж, поехали
@ -3007,7 +3067,7 @@ Sshh... It's a super-secret.
+ Модуль `antigravity` - одно из немногих пасхальных яиц, выпущенных разработчиками Python.
+ `import antigravity` открывает веб-браузер, указывающий на [классический комикс XKCD](https://xkcd.com/353/) о Python.
+ Это еще не все. Внутри пасхального яйца находится **еще одно пасхальное яйцо**. Если вы посмотрите на [код](https://github.com/python/cpython/blob/master/Lib/antigravity.py#L7-L17), там определена функция, которая якобы реализует [алгоритм геохашинга XKCD](https://xkcd.com/426/).
+ Это еще не все. Внутри пасхального яйца находится **еще одно пасхальное яйцо**. Если вы посмотрите на [код](https://github.com/python/cpython/blob/master/Lib/antigravity.py#L7-L17), там определена функция, которая якобы реализует алгоритм [XKCD](https://xkcd.com/426/).
---
@ -3164,7 +3224,7 @@ True
### ▶ Да, оно существует!
<!-- Example ID: 4286db3d-1ea7-47c9-8fb6-a9a04cac6e49 --->
**Ключевое слово `else` в связвке с циклом `for`.** Один из стандартных примеров:
**Ключевое слово `else` в связке с циклом `for`.** Один из стандартных примеров:
```py
def does_exists_num(l, to_find):
@ -3239,7 +3299,7 @@ Ellipsis
- Многоточие может использоваться в нескольких случаях,
+ В качестве заполнителя для кода, который еще не написан (аналогично оператору `pass`)
+ В синтаксисе срезов (slices) для представления полных срезов в оставшемся направлении
``py
```py
>>> import numpy as np
>>> three_dimensional_array = np.arange(8).reshape(2, 2, 2)
array([
@ -3264,7 +3324,7 @@ Ellipsis
[5, 7]])
```
Примечание: это будет работать для любого количества измерений. Можно даже выбрать срез в первом и последнем измерении и игнорировать средние (`n_dimensional_array[firs_dim_slice, ..., last_dim_slice]`)
+ В [подсказках типов](https://docs.python.org/3/library/typing.html) для указания только части типа (например, `(Callable[..., int]` или `Tuple[str, ...]`))
+ В [подсказках типов](https://docs.python.org/3/library/typing.html) для указания только части типа (например, `Callable[..., int]` или `Tuple[str, ...]`)
+ Вы также можете использовать `Ellipsis` в качестве аргумента функции по умолчанию (в случаях, когда вы хотите провести различие между сценариями "аргумент не передан" и "значение не передано").
---
@ -3483,7 +3543,7 @@ def square(x):
## Секция: Разное
### ▶ `+=` быстрее
### ▶ `+=` быстрее `+`
<!-- Example ID: bfd19c60-a807-4a26-9598-4912b86ddb36 --->
```py
@ -3587,7 +3647,7 @@ def convert_list_to_string(l, iters):
>>> %timeit -n100 add_string_with_plus(1000)
388 µs ± 22.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> %timeit -n100 add_string_with_plus(10000) # Quadratic increase in execution time
>>> %timeit -n100 add_string_with_plus(10000) # Квадратичное увеличение времени выполнения
9 ms ± 298 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
```
@ -3627,7 +3687,7 @@ KeyError: 1
#### 💡 Объяснение:
+ В CPython есть общая функция поиска по словарю, которая работает со всеми типами ключей (`str`, `int`, любой объект ...), и специализированная для распространенного случая словарей, состоящих только из `str`-ключей.
+ Специализированная функция (названная `lookdict_unicode` в [исходный код CPython](https://github.com/python/cpython/blob/522691c46e2ae51faaad5bbbce7d959dd61770df/Objects/dictobject.c#L841)) знает, что все существующие ключи (включая искомый ключ) являются строками, и использует более быстрое и простое сравнение строк для сравнения ключей, вместо вызова метода `__eq__`.
+ Специализированная функция (названная `lookdict_unicode` в [CPython](https://github.com/python/cpython/blob/522691c46e2ae51faaad5bbbce7d959dd61770df/Objects/dictobject.c#L841)) знает, что все существующие ключи (включая искомый ключ) являются строками, и использует более быстрое и простое сравнение строк для сравнения ключей, вместо вызова метода `__eq__`.
+ При первом обращении к экземпляру `dict` с ключом, не являющимся `str`, он модифицируется, чтобы в дальнейшем для поиска использовалась общая функция.
+ Этот процесс не обратим для конкретного экземпляра `dict`, и ключ даже не обязательно должен существовать в словаре. Поэтому попытка неудачного поиска имеет тот же эффект.
@ -3706,7 +3766,7 @@ def dict_size(o):
* Несколько странных, но семантически правильных утверждений:
+ `[] = ()` - семантически корректное утверждение (распаковка пустого `кортежа` в пустой `список`)
+ `'a'[0][0][0][0][0]` также является семантически корректным утверждением, поскольку в Python строки являются [последовательностями](https://docs.python.org/3/glossary.html#term-sequence)(итерируемыми объектами, поддерживающими доступ к элементам с использованием целочисленных индексов).
+ `'a'[0][0][0][0][0]` также является семантически корректным утверждением, поскольку в Python строки являются [последовательностями](https://docs.python.org/3/glossary.html#term-sequence) (итерируемыми объектами, поддерживающими доступ к элементам с использованием целочисленных индексов).
+ `3 --0-- 5 == 8` и `--5 == 5` - оба семантически корректные утверждения и имеют значение `True`.
* Учитывая, что `a` - это число, `++a` и `--a` являются корректными утверждениями Python, но ведут себя не так, как аналогичные утверждения в таких языках, как C, C++ или Java.