{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "381820da",
   "metadata": {},
   "source": [
    "# Объектная структура в Python #"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "405c46bd",
   "metadata": {},
   "source": [
    "Сейчас давайте поговорим про объектную структуру в Python.\n",
    "\n",
    "На протяжении всей недели я подчеркивал, что когда мы создаем переменную, мы не просто присваиваем переменной значение, а мы создаем связь имени переменной с объектом.\n",
    "\n",
    "Давайте посмотрим, что это значит. Посмотрим на примере целочисленного объекта. Давайте объявим переменную `num` и свяжем ее с целочисленным объектом 13."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "38ec2a88",
   "metadata": {},
   "outputs": [],
   "source": [
    "num = 13"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "479473c5",
   "metadata": {},
   "source": [
    "Если посмотреть внимательней, то окажется, что у переменной `num` есть метод `__add__` c двумя подчеркиваниями, который добавляет к числу другое число."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "626c1f15",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "15"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "num.__add__(2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4585f708",
   "metadata": {},
   "source": [
    "Как же так? Что это за метод? В других языках программирования вы могли видеть, что когда вы создаете переменную, эта переменная на самом деле ссылается непосредственно на адрес памяти, в котором хранится, например, число. В Python не так.\n",
    "\n",
    "В Python переменная ссылается на объект, и если мы посмотрим внимательней на наш объект и используем функцию `dir` для того, чтобы посмотреть все методы атрибута, то мы увидим, что на самом деле их еще больше, чем мы могли бы подумать. Их действительно очень много."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "dd2ae309",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'as_integer_ratio', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']\n"
     ]
    }
   ],
   "source": [
    "print(dir(num))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "77fa3764",
   "metadata": {},
   "source": [
    "Если мы то же самое сделаем со строкой, то мы увидим, что и для строки выполняется то же самое."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "05b3e0c9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']\n"
     ]
    }
   ],
   "source": [
    "print(dir(\"строка\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c9225a3e",
   "metadata": {},
   "source": [
    "Дело в том, что в Python всё есть объект. Целые числа, строки — это все представлено, как объект внутри. На C уровне определена структура, которая называется `PyObject`.\n",
    "\n",
    "```c\n",
    "typedef struct _object {\n",
    "    _PyObject_HEAD_EXTRA\n",
    "    Py_ssize_t ob_refcnt;        // Счетчик ссылок\n",
    "    struct _typeobject *ob_type; // Указатель на тип объекта\n",
    "} PyObject;\n",
    "```\n",
    "\n",
    "Эта структура содержит два важных поля, первое из которых это `ob_refcnt` — это счетчик ссылок. Что это такое?\n",
    "Когда мы создаем новую связь имени переменной с объектом, этот счетчик ссылок автоматически увеличивается на единицу. Как только создается еще одна связь — еще на единицу, и так далее. Когда же связь становится не нужна, например, функция закончила выполнение, и Python понимает, что какая-то переменная больше не нужна, счетчик ссылок, на который она ссылается, на единицу уменьшается, и когда он достигает нуля, счетчик ссылок может удалить объект из памяти. Таким образом, в Python автоматическая сборка мусора со счетчиком ссылок.\n",
    "\n",
    "Второе важное поле — это указатель на тип объекта. Зачем это нужно? Как мы уже говорили, Python — это язык с динамической типизацией, и указатель на тип объекта, который есть у каждого объекта в Python, позволяет Python'у в процессе работы в runtime'е определять тип того или иного объекта, тем самым производя правильные операции, применяя правильные действия к этому объекту.\n",
    "\n",
    "Также есть структура `PyVarObject`, которая расширяет структуры `PyObject` и добавляет одно важное поле.\n",
    "\n",
    "```c\n",
    "typedef struct {\n",
    "    PyObject ob_base;\n",
    "    Py_ssize_t ob_size; // Кол-во элементов в переменной части\n",
    "} PyVarObject;\n",
    "```\n",
    "\n",
    "Это поле `ob_size`. Поле `ob_size` содержит количество элементов переменной части объекта. Что это значит? Это значит, что это поле, например, может использоваться как контейнер для длины строки. Для того чтобы каждый раз не рассчитывать длину строки, не итерироваться по всем символам, длину строки можно сохранять в это поле.\n",
    "\n",
    "Это поле используется в различных типах по-разному для строки, как мы видим, для хранения длины.\n",
    "\n",
    "Так же на C уровне определены два макроса, которые соответствуют `PyVarObject` и `PyObject`, и эти макросы используются для того, чтобы создавать новые типы в Python, новые объекты.\n",
    "\n",
    "```c\n",
    "#define PyObject_HEAD PyObject ob_base;\n",
    "#define PyObject_VAR_HEAD PyVarObject ob_base;\n",
    "\n",
    "typedef struct PyMyObject {\n",
    "    PyObject_HEAD\n",
    "    ...\n",
    "}\n",
    "```\n",
    "\n",
    "или\n",
    "\n",
    "```c\n",
    "typedef struct PyMyObject {\n",
    "    PyObject_VAR_HEAD\n",
    "    ...\n",
    "}\n",
    "```\n",
    "\n",
    "По сути так реализованы все встроенные типы в Python. Они расширяют структуры `PyObject`, либо `PyVarObject`. Тем самым, у каждого объекта есть счетчик ссылок, у каждого объекта есть указатель на тип.\n",
    "\n",
    "Что стоит отметить еще, что так реализованы не только какие-то простые типы, такие как `int`, `float`, `boolean` типы, но и другие вещи, которые вы встречаете в Python'е. Например, модули, либо функции, либо классы. Все это является под капотом объектом со своими методами и атрибутами.\n",
    "\n",
    "Теперь, я думаю, вас не испугает огромное количество методов, которые есть у питоновских объектов. Вы знаете, что на самом деле под капотом все это является расширением структуры `PyObject`, и, что самое главное, из этого следует, то что в Python'е любой объект можно присвоить переменной, можно передать как аргумент в функцию. На самом деле знать это необязательно для того, чтобы программировать на Python'е. Однако это информация, которая поможет вам в дальнейшем стать более продуктивным, все-таки знать, как инструмент устроен. Это очень важно."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
