diff --git a/translations/README-ru.md b/translations/README-ru.md index 325980a..3e7170b 100644 --- a/translations/README-ru.md +++ b/translations/README-ru.md @@ -25,6 +25,8 @@ PS: Если вы уже читали **wtfpython** раньше, с измен - [Секция: Напряги мозги!](#секция-напряги-мозги) - [▶ Первым делом!](#-первым-делом) - [💡 Обьяснение](#-обьяснение) + - [▶ Строки иногда ведут себя непредсказуемо](#-строки-иногда-ведут-себя-непредсказуемо) + - [💡 Объяснение](#-объяснение) # Структура примера @@ -205,3 +207,80 @@ if a := some_func(): - Аналогично, `(a, b := 16, 19)` эквивалентно `(a, (b := 16), 19)`, которое есть не что иное, как кортеж из 3 элементов. --- + +### ▶ Строки иногда ведут себя непредсказуемо + + +1\. + +```py +>>> a = "some_string" +>>> id(a) +140420665652016 +>>> id("some" + "_" + "string") # Обратите внимание, оба идентификатора одинаковы +140420665652016 +``` + +2\. + +```py +>>> a = "wtf" +>>> b = "wtf" +>>> a is b +True + +>>> a = "wtf!" +>>> b = "wtf!" +>>> a is b +False +``` + +3\. + +```py +>>> a, b = "wtf!", "wtf!" +>>> a is b # Актуально для версий Python, кроме 3.7.x +True + +>>> a = "wtf!"; b = "wtf!" +>>> a is b # Выражение вернет True или False в зависимости вызываемой среды (python shell / ipython / скрипт). +False +``` + +```py +# На этот раз в файле +a = "wtf!" +b = "wtf!" +print(a is b) + +# Выводит True при запуске модуля +``` + +4\. + +**Output (< Python3.7 )** + +```py +>>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa' +True +>>> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa' +False +``` + +Логично, правда? + +#### 💡 Объяснение + +- Поведение в первом и втором фрагментах связано с оптимизацией CPython (называемой интернированием строк ((англ. string interning))), которая пытается использовать существующие неизменяемые объекты в некоторых случаях вместо того, чтобы каждый раз создавать новый объект. +- После "интернирования" многие переменные могут ссылаться на один и тот же строковый объект в памяти (тем самым экономя память). +- В приведенных выше фрагментах строки неявно интернированы. Решение о том, когда неявно интернировать строку, зависит от реализации. Правила для интернирования строк следующие: + - Все строки длиной 0 или 1 символа интернируются. + - Строки интернируются во время компиляции (`'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)). +- Единица компиляции в интерактивной среде 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). + +---