Как работает интерпретатор Python при динамической типизации?
Я прочитал этот вопрос, но он не дал мне однозначного ответа: как интерпретатор Python ищет типы?
Как интерпретатор Python узнает тип переменной? Я не смотрю, как получить тип. Я здесь смотрю на то, что происходит за сценой. В приведенном ниже примере, как он связывает класс int или строку с моей переменной.
Откуда он знает, что это int:
>>> i = 123
>>> type(i)
<class 'int'>
или эта строка:
>>> i = "123"
>>> type(i)
<class 'str'>
3 ответа
как это связывает класс int или строку с моей переменной
Python нет. Переменные не имеют типа. Только объект, на который ссылается переменная, имеет тип. Переменные - это просто имена, указывающие на объекты.
Например, следующее также показывает тип объекта, но переменная не используется:
>>> type(1)
<class 'int'>
>>> type('foobar')
<class 'str'>
Когда вы используете type(variable)
, variable
часть выражения просто возвращает объект, на который ссылается имя, передавая объект type()
функция. Когда используешь 1
или же 'foobar'
выражение является литералом, создающим объект, который затем передается type()
функция.
Объекты Python - это просто структуры данных в памяти интерпретатора; в CPython C используются структуры. Переменные - это просто ссылки (указатели) на эти структуры. Базовая структура типов в CPython называется PyObject
и эта структура имеетob_type
слот, который говорит Python, что типа что-то. Типы - это просто C-структуры.
Если вы хотите следовать исходному коду CPython, вы должны начать сbltinmodule.c
исходный код(сtype
является встроенным именем), который определяет type
как PyType_Type
структура. Вызовтипа(type
это тоже тип) вызывает ихtp_new
функция иPyType_Type
определяет это как type_new
функция Эта функция обрабатывает вызовы с одним аргументом следующим образом:
/* Special case: type(x) should return x->ob_type */
{
const Py_ssize_t nargs = PyTuple_GET_SIZE(args);
const Py_ssize_t nkwds = kwds == NULL ? 0 : PyDict_Size(kwds);
if (PyType_CheckExact(metatype) && nargs == 1 && nkwds == 0) {
PyObject *x = PyTuple_GET_ITEM(args, 0);
Py_INCREF(Py_TYPE(x));
return (PyObject *) Py_TYPE(x);
}
Вот x
это PyObject
объект, который вы передали; обратите внимание, не переменная, а объект! Так что для вашего 1
целочисленный объект или 'foobar'
строковый объект, Py_TYPE()
результат макроса возвращается. Py_TYPE
это макрос, который просто возвращает ob_type
значение любого PyObject
структура.
Итак, теперь у вас есть тип объекта для 1
или же 'foobar'
; как ты видишь <class 'int'>
или же <class 'str'>
в вашей сессии переводчика? Интерактивный интерпретатор Python автоматически использует repr()
функция на любое выражение результатов. В структуре C для PyType_Type
определения PyType_Type
структура включена, так что все слоты для этого типа доступны напрямую; Я опущу здесь, как именно это работает. Для объектов типа, используя repr()
означает type_repr
вызывается функция, которая возвращает это:
rtn = PyUnicode_FromFormat("<class '%s'>", type->tp_name);
Итак, в конце концов, type(1)
получает ->ob_type
слот, (который оказывается PyLong_Type
структура в Python 3, длинная история), и эта структура имеет tp_name
слот установлен на"int"
,
TL; DR: переменные Python не имеют типа, они просто указатели на объекты. У объектов есть типы, и интерпретатор Python будет следовать серии косвенных ссылок, чтобы получить имя типа для печати, если вы повторяете объект в вашем интерпретаторе.
Переменные Python не имеют типа, они просто ссылки на объекты. Размер ссылки одинаков независимо от того, на что он ссылается. В реализации CPython это указатель, и у него есть тип, это указатель на объект Python: PyObject *
, Указатель одного типа независимо от класса объекта. Объекты, с другой стороны, знают, к какому классу они принадлежат.
Утверждалось, что в Python нет переменных, только имена, хотя для большинства людей это слишком далеко.
Ссылки в реализации CPython имеют идентификатор (идентификатор), который фактически является виртуальным адресом. Детальность и ценность этого адреса не заслуживают внимания - он может (и, вероятно, будет) изменяться между версиями и не предназначен для использования для чего-либо, кроме уникального номера, идентифицирующего объект. Тем не менее он может предоставить интересные указатели (простите за каламбур) на то, что происходит:
>>> x = 42
>>> y = x
>>> id(x)
4297539264
>>> id(y)
4297539264
Обратите внимание, что идентификатор (адрес) x
а также y
одинаковы - они ссылаются на один и тот же объект, int
со значением 42. Итак, что происходит, когда мы меняем x
делает y
изменить также?
>>> x = "hello"
>>> id(x)
4324832176
>>> id(y)
4297539264
К счастью, нет. Сейчас x
просто ссылается на новый объект класса str
со значением "Hello".
Когда мы:
>>> id(y)
4297539264
>>> y = 37
>>> id(y)
4297539104
Идентификатор y
изменилось! Это потому, что теперь он ссылается на другой объект. int
s являются неизменными, поэтому назначение y = 37
не изменил исходный объект (42) он создал новый. У объекта со значением 42 счетчик ссылок уменьшен и теперь (теоретически) может быть удален. На практике это, вероятно, останется в памяти по соображениям эффективности, но это деталь реализации.
Тем не мение:
>>> a = [1,2,3,4]
>>> b = a
>>> id(a)
4324804808
>>> id(b)
4324804808
>>> a[0] = 99
>>> b
[99, 2, 3, 4]
Так что меняя список a
изменился b
! Это потому, что списки изменчивы, они могут меняться. Назначение b = a
только скопировал ссылку, а не список. Смотрите копию в стандартной библиотеке.
Понятие "тип" переменной "реализуется" с использованием объектов определенного класса.
Так в
a=float()
объект типа float
как определено классом float
возвращается float()
, Python знает, что это за тип, потому что так работают объекты: вы знаете, какой это тип. a
сейчас float
объект со значением 0.0.
Со встроенными, это то же самое, просто у них есть краткая информация для их объявления.
i=123
такой же как
i=int(123)
int()
возвращает объект класса integer со значением 123.
так же
i="123"
такой же как
i=str("123")
str("123")
возвращает объект класса str со значением "123"