Биты байты и вообще что там под капотом

Эту часть можно смело пропустить, но она позволит заглянуть под капот Питону и компьютеру, и получить некоторое более полное представление о том, что происходит, когда мы запускаем программу на выполнение.
В предыдущей статье мы выяснили, что переменные объявлять просто. Достаточно придумать имя и присвоить ему значение. Ну а смысл? Это как писать на стене маркером слово «Стена», занятно, но бессмысленно.
Давайте разберемся с этим подробнее, и найдем какое-то практическое применение. Но сначала нужно разобраться с типами.


Типы переменных. Не путаем мух с котлетами.


Как мы уже знаем, переменная – это указатель на участок памяти, в котором хранится некоторое значение. В этом участке памяти может лежать число, строка, какой-то сложный объект навроде даты, генератор, который возвращает новые значения по запросу и т.п. В общем-то там может лежать что угодно, все, что программист посчитал нужным запихнуть. Причем, с течением времени, может меняться как само значение, так и его тип.
К примеру, жил-был шпион. Его звали Витя. Его отправили под прикрытием куда-то на Восток, где он жил под другим именем, но там его разоблачили, посадили в тюрьму, и теперь он заключенный № 100501.

# Шпион изначально
spy_name = "Витя"
print(spy_name)
# Шпион под прикрытием
spy_name = "Iku Li Zci"
print(spy_name)
# Шпиона поймали и посадили в секретную тюрьму
spy_name = 100501
print(spy_name)

Таким образом, переменная spy_name  из строки «Витя» стала числом 100501. Для обычного человека в этом нет никакой проблемы, для него и то и другое является либо текстом, который он видит на экране или бумаге, либо звуком, который он слышит ушами. В любом случае, человек узнал информацию, куда-то себе её отложил и не сломался.
С компьютерами и языками программирования все немного сложнее. В некоторых языках такой финт – изменение переменной со строки в число или наоборот просто не прошел бы, программа бы не стала работать или вылетела с ошибкой. Питон к этому относится проще, допуская что программист знает, что делает, и ругается только изредка, при обработке таких переменных. 
Например, вот этот кусок кода отработает без ошибок:

spy_name = "Витя"
print("Шпиона зовут " + spy_name)


А вот этот вызовет ошибку:

spy_name = 100501
print("Шпиона зовут " + spy_name)


 
Почему?
Потому что в первом случае, когда я написал 

print("Шпиона зовут " + spy_name)

Питон проанализировал, чего я от него хочу, и выстроил примерно такую цепочку рассуждений:

«Человек хочет, что бы я вызвал функцию print, которая выведет информацию на экран. Хорошо, такая функция есть. 
Человек хочет, что бы эта функция напечатала результат сложения двух вещей "Шпиона зовут " и переменной spy_name. 
Первая вещь строка, и второй лежит  "Витя" – это тоже строка. 
Значит, человек хочет, что бы я сложил две строки. 
Я умею это делать. 
Когда меня просят сложить две строки, я беру все символы первой строки и в конце добавляю все символы второй строки. В результате у меня получается новая строка. 
Эту строку я отдаю функции print.
Строка печатается.
Работа выполнена.
»


Во втором случае, когда значение переменной spy_name стало равно 100501, цепочка рассуждений вызвала некоторый клин в работе анализатора. Наверное как то так:


«Человек хочет, что бы я вызвал функцию print, которая выведет информацию на экран. Хорошо, такая функция есть. 
Человек хочет, что бы эта функция напечатала результат сложения двух вещей "Шпиона зовут " и переменной spy_name. 
Первая вещь строка, во второй лежит  100501 – это число. 
Wait what? 
Нужно взять буквы и приплюсовать к ним цифры? Человек имел в виду, что 100501 можно воспринимать как строку? Нужно перевести числа в аналогичные им символы? Или это что-то с байтами? Или человек имел в виду, что нужно первую строку воспринять как число и приплюсовать к нему второе по правилам арифметики? Тогда надо из первой строки выкинуть все, что не является числами, это ли требуется? В общем, пускай явно укажет, что требуется.
Напишу так:
TypeError: must be str, not int
»


И вот когда программист явно напишет, что хочет, чтобы число воспринималась как строка, то только тогда все заработает.
spy_name = 100501

print("Шпиона зовут " + str(spy_name))

Реклама



 
Что происходит под капотом:


Как всем нам известно, внутри компьютера все представлено в виде нулей и единиц. Но, как правило, люди запоминают это как факт, считают какой-то дикой странной магией и объяснить, почему так, не могут. А на самом деле, все просто.
Единица и ноль – это наличие и отсутствие электрического сигнала. Иногда магнитного. 


Возьмем, например, жесткий диск вашего компьютера (hdd).  Это коробка, внутри которой расположены круглые пластинки и считывающие головки. Кто помнит граммофоны и проигрыватели – принцип тот же, только пластины из металла. Вот так выглядит жесткий диск изнутри:


 
Когда нужно записать информацию на диск, головка двигается к нужному месту на диске, сам диск тоже подкручивается нужным местом к головке, после чего это место намагничивается (ставится единица). Если нужно удалить информацию – размагничивается (ноль). Для считывания информации, головка елозит туда-сюда, получая данные – намагничено это место или нет (11100101100011….). 


Если взять оперативную память, то там не нет вращающихся частей, но есть микросхемы, в которых есть микроскопические электрические замочки, которые либо открыты, либо закрыты. В зависимости от этого считается, единица или ноль находится в этой ячейке.


В центральном процессоре десятки миллионов нанотранзисторов, которые принимают на вход электрический сигнал или его отсутствие, и транслируют дальше этот электрический сигнал в измененном или неизменном виде. 


Окей-окей, но как эти единицы и нули сочетаются с цифрами и буквами?


По-разному (поэтому Питон и ругается). Для хранения чисел требуется не так много ячеек памяти, т.к. числа из десятичной системы счисления (в которой мы живем) переводить в двоичную не так уж сложно.
Представим, что внутри своей оперативной памяти вы выделили кусочек для хранения какого-то числа, и сейчас он пуст – все ячейки свободны. 
 


Мы выделили 8 ячеек, в каждой из которых заряд либо есть, либо его нет. Каждая такая ячейка называется битом (bit). Слово образовано от словосочетания binary digit, что переводится как двоичное число. Двоичное – потому что содержит максимум два значения (1 либо 0, есть заряд/нет заряда).


Для удобства такие биты берут пачками по 8 штук (что в сумме образует байт), и мы получаем то, что видим на картинке выше – один пустой байт. 8 ячеек, каждая из которых может хранить либо один, либо ноль. Но число таких комбинаций нулей и единиц в одном байте составляет 256 значений! Т.е. когда все 8 ячеек пусты, это первая комбинация. Когда заполнена только первая ячейка, это вторая комбинация, когда первая пустая а вторая заполнена, это третья комбинация и т.п. 


Выработав несложные правила, мы сможем выстроить соответствие таких ячеек десятичным значениям. Логично, что если ни одно поле не заполнено, то там хранится 0. Если заполнено только одно то там хранится единица. 


Как быть с остальными комбинациями? Идем справа налево, и используем степени числа 2. Как мы знаем, любое число в степени 0 дает единицу, в степени 1 дает само себя, в степени два умножается само на себя и т.п. Если кто-то забыл, Excel поможет, степени двойки будут выглядеть так:


 

  Итак, мы идем справа налево, и умножаем значение ячейки на соответствующую степень двойки, результат суммируем. Т.е. крайнее правое значение мы умножаем на 1, второе умножаем на 2, третье умножаем на 3, и т.п. И в итоге единицы и нули мы переводим в привычные нам числа.  


Для перевода десятичного числа в двоичное используется тот же алгоритм в обратном порядке (число делится на степени двойки). 


На практике, для хранения чисел выделяется сразу несколько байт, причем первый бит слева обозначает знак (положительное или отрицательное число), таким образом, к нашему 8битному числу из примера мы можем прибавить слева еще несколько клеток, каждая клетка будет увеличивать максимально допустимое значение в два раза. Подробнее можно прочитать тут


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


Изначально это были таблицы символов ASCII:

 


Как видите, 127 символов (выделено 7 бит), каждый представлен числом. Для русского или любого другого алфавита добавляется 8 бит, что дает еще 127 допустимых значений, куда и заносятся символы. Сейчас эти таблицы постепенно вытесняет Unicode – по сути такие же таблицы, но сделаны поумнее, ну и плюс в каждом интерпретаторе/компиляторе есть как свои кодировочные схемы, так и используются таблицы операционной системы. Все это долгая и запутанная история.
В общем, когда вы храните строку, она точно так же преображается в нули и единицы, каждый символ кодируется и декодируется в соответствии с определенными правилами. Когда приходит очередь программы что-то сделать с данными, она может впасть в ступор – для программы неясно, что это: 01001 – число или строка или что вообще? Ну и предполагается, что программист умный, он подскажет.



Категория: Программирование
Последнее изменение:

Не забудьте рассказать друзьям об этой статье.
Чтобы поддержать ресурс Bablofil достаточно просто поделиться с друзьями этой статьей в социальных сетях. Каждый репост - это самая высокая оценка качества материала. Спасибо, что читаете этот блог.



Комментарии
Пожалуйста, авторизуйтесь, что бы оставить свой комментарий
Крипто-кошельки для помощи и благодарности проекту:

Bitcoin адрес проекта: [[address]]

Перевод на сумму [[value]] BTC получен. Спасибо!.
[[error]]

Ethereum адрес проекта: [[address]]