[МУЗЫКА] [МУЗЫКА] Сейчас мы поговорим подробнее о математических и технических моментах. Начнем с конкретного объяснения, как устроено деление и взятие остатка от деления. Мы уже использовали это на практике, но посмотреть, как оно работает и устроено внутри, конечно же, нужно. Пусть у нас есть два числа: A и B. В переменную C мы сохраним целую часть от деления A // B, а в переменную D — остаток от деления. Естественно, при этом B не должно быть равно 0, потому что делить на 0 нельзя. Кстати, это можно установить опытным путем, если вы не знаете, можно или нельзя, в программировании всегда можно попробовать. Если у нас выполнены такие условия, то есть в C и D положены, соответственно, целая часть и остаток от деления, то должно выполняться следующее равенство: целая часть от деления, у нас это C, умноженная на B, на делитель, плюс остаток от деления, должно в сумме дать нам наше делимое, число A. При этом должны выполняться следующие утверждения. Если делитель число B был больше 0, если положительно оно, то остаток должен быть не отрицательным. То есть остаток, наша переменная D, должен быть больше либо равно 0, [БЕЗ_ЗВУКА] и в то же время меньше, чем наш делитель, то есть D меньше, чем B. Строго меньше, то есть остаток не может быть равен делителю, он всегда строго меньше. В случае если мы делили на отрицательное число, в жизни это встречается достаточно редко, но в математике это вполне корректно, у нас будет действовать немножко другая схема. Запишем. Если B, делитель, был меньше 0, то остаток также должен быть меньше 0 и не превосходить наш делитель по модулю. Кстати, он может быть равен 0, я немного оговорился, условие будет выглядеть очень похоже, но но для отрицательных чисел. То есть D, остаток, меньше либо равен 0, и в тоже время он должен быть больше, чем наше число B. B отрицательное, то есть по модулю наш остаток меньше, а абсолютное значение больше. Например, если мы делим на −5, то остаток может быть равен 0 или −3, а вот −5 или −6 быть не может. Соответственно, получается, что условие выглядит как D > B. Пользуясь этими соотношениями, можно понять, какие получатся целые части от деления, остатки от деления для любого набора чисел: положительного, отрицательного и любого их сочетания, то есть положительное-положительное, положительное-отрицательное, и все четыре случая, таким образом, мы сможем рассмотреть. Посмотрим один маленький пример. Например, −5 разделить на −3 — это достаточно редкая ситуация в реальной жизни, когда у нас делится отрицательное число на отрицательное, делится все-таки чаще на положительное. Давайте проверим по этим формулам, что у нас должно получиться. Нас интересует целая часть от деления, то есть переменная C, и остаток тоже для интереса посчитаем. Итак, какой случай у нас здесь есть? B < 0, делитель. Таким образом, остаток в этой ситуации должен быть отрицательным числом. Какой у нас может быть отрицательный остаток, чтобы выполнялось нужное нам равенство? Единственный подходящий случай, это когда у нас целая часть от деления равна 0, а остаток от деления тех же чисел −5 и −3 должен быть равен, чтобы выполнялось наше равенство... Давайте подумаем, может быть, мы посчитали неправильно. Я немного запишу здесь. Целая часть от деления, в нашем случае я напишу прямо рядом с нашим равенством, мы делим на −3, должны получить −5, а число D не должно превосходить по модулю −5. Как же нам это сделать? Например, если мы возьмем C = 1, здесь получим −3, и нам, чтобы получить −5, нужно добавить −2. Проверим это. Итак, у нас есть случай, когда B отрицательно, остаток от деления должен быть также отрицательным, у нас он получается отрицательным, значит, целая часть от деления равна 1, а не 0, как я записал. Ничего страшного, все могут ошибиться. Давайте исправим, это у нас не 0, а это у нас, оказывается, 1, а остаток от деления, оказывается, равен −2. Таким образом, как вы видите, несмотря на то, что у меня иногда возникает потребность разделить на отрицательные числа, я тоже не с ходу это делаю. Но, единственное, что радует, что действительно такие задачи встречаются редко, и всегда, пользуясь этими формулами, можно проверить. Теперь перейдем к другой части, а именно как хранятся переменные в языке Python, как это все устроено внутри, и это поможет нам в будущем понимать гораздо более сложные вещи. Итак, запишем такую программу, достаточно простую. Переменная A = 1. Что происходит в этот момент? У нас где-то в памяти создается объект типа целое число со значением 1. Затем к этому объекту привязывается имя A, то есть где-то в пространстве имен у нас создается такой ярлычок с буквой A, и в результате присваивания этот ярлычок привязывается. Можно нарисовать такую ниточку, которая ведет от нашей переменной к нашему значению. У нас в памяти не было объекта со значением 1, он создался, и к нему привязался ярлычок A. Если теперь мы создадим еще одну переменную с тем же самым значением. Например, создадим переменную B со значением 1, что при этом произойдет? Поскольку объект 1 уже есть в памяти, то создаваться заново он не будет, то есть у нас создастся только новый ярлычок с надписью B, который привяжется к тому же самому объекту, [БЕЗ_ЗВУКА] к 1. Если мы теперь изменим значения, например, мы напишем, что A = 2. [БЕЗ_ЗВУКА] Что будет происходить? Если бы числа были изменяемыми, то у нас эта 1 должна была бы превратиться в 2. Но мы знаем, что числа — это неизменяемый тип данных, а значит, у нас создастся новый объект, это будет 2, ее в нашей памяти еще пока нет, и старая ссылка, которая ведет на 1, исчезнет, [БЕЗ_ЗВУКА] и появится новая ссылка, которая указывает на 2, то есть теперь переменная A привязана уже не к 1, не к объекту 1, а к объекту… 2, а B по-прежнему равно 1, его значение не изменилось. То есть каждая переменная в языке Python независимо от того, какой тип она имеет, является ссылкой. И ссылкой на объект либо изменяемый, таких мы пока не знаем, либо неизменяемый. Если объект неизменяемый, то понятно, что происходит. Давайте посмотрим еще один маленький момент. Пусть наше B стало равно, например, 3. Мы создаем новый объект 3, опять же, в памяти. Ссылка, которая вела из B в 1, удаляется, поскольку значение изменилось. Вот она исчезает, наша ссылка, [БЕЗ_ЗВУКА] и появляется ссылка на 3, на новое значение. Теперь у нас на 1 не показывает ни одна ссылка. У каждого объекта в памяти есть счетчик ссылок, то есть сколько ссылок указывает на этот объект. У 1 они кончились: у нас было две ссылки, и обе удалились. После этого происходит сборка мусора. Объект уже, никак мы его достать не можем, никто не ссылается на него, мы не можем вытянуть его ни за один ярлычок. После этого объект может быть удален. То есть как только ссылки кончились, объект удаляется, и в памяти может, в принципе, уже отсутствовать. Иногда они некоторое время живут в памяти и удаляются в процессе сборки мусора. И еще один, совсем маленький момент — это когда у нас происходит вычисление какого-то выражения арифметического. Например, очень простое арифметическое выражение: 2 * 3 + 1. И пусть мы кладем это в какую-то переменную: c = 2 * 3 + 1. Как будет происходить вычисление этого выражения? Создадутся объекты, или, если они уже были в памяти, то они будут использованы, со значениями 2, 3 и 1. Затем создастся временный объект, который получится в результате операции 2 * 3. Ко вре́менным объектам у нас нет ссылок, нет ярлычков, мы их вытянуть не можем, они просто есть в памяти в какой-то момент и удаляются, как только становятся не нужны, пока они не нужны. Затем, когда у нас получилось 6, согласно приоритетам операции мы осуществляем операцию сложения и получаем объект 7. И вот этот объект уже, пока что он временный, но он привязывается, как результат вычисления, к нашей переменной c и остается в памяти. Значит, вот так происходит процесс вычисления арифметического выражения, и все вре́менные объекты, которые у нас были, если на них никто не ссылается, никакие другие переменные, они могут быть в этот момент удалены. [БЕЗ_ЗВУКА] Допустим, на 3, 1 и 6 кто-то ссылался. Так происходит процесс вычисления арифметических выражений в Python. В принципе, со строками происходит абсолютно то же самое, то есть строки лежат также в памяти, и на них могут указывать ссылки, ведущие от переменных. Нужно понимать, какой процесс происходит при изменении, например, содержимого переменной неизменяемого типа, что на самом деле происходит создание нового объекта и просто отвязка ссылки и привязка к новому объекту. Когда мы научимся работать с изменяемыми объектами, мы немного вернемся к этой теме, чтобы было понятно, по какому принципу что-то у нас в программе происходит. [МУЗЫКА] [МУЗЫКА]