【问题标题】:How does Python interpreter work in dynamic typing?Python解释器如何在动态类型中工作?
【发布时间】:2016-11-16 22:29:39
【问题描述】:

我读了这个问题,但没有给我明确的答案: How does Python interpreter look for types?

python解释器如何知道变量的类型?我不是在寻找如何获取类型。我在这里看看幕后发生的事情。在下面的示例中,它如何将类 int 或 string 关联到我的变量。

它怎么知道这是一个int:

>>> i = 123
>>> type(i) 
<class 'int'>

或者那个字符串:

>>> i = "123"
>>> type(i)
<class 'str'>

【问题讨论】:

  • @GreenAsJade:OP 使用的是 Python 3,其中 type 对象的表示使用 'class',而不是 'type';这样做是为了反映 C 定义的类型也只是类。
  • @MartijnPieters 那么可能需要一个 python3 标签?
  • @GreenAsJade:不,Python 2 和 3 中的答案是一样的。但是提供的输出不需要更正。
  • 嗯,好的。但是字符串示例确实:)

标签: python python-internals dynamic-typing


【解决方案1】:

它如何将类 int 或字符串关联到我的变量

Python 没有。 变量没有类型。只有变量引用的对象才有类型。变量只是指向对象的名称

例如下面也显示了一个对象的类型,但是没有涉及到变量

>>> type(1)
<class 'int'>
>>> type('foobar')
<class 'str'>

当您使用type(variable) 时,表达式的variable 部分仅返回名称引用的对象,将对象 传递给type() 函数。使用1'foobar' 时,表达式是产生对象的文字,然后将其传递给type() 函数。

Python 对象只是解释器内存中的数据结构;在 CPython 中使用 C 结构。变量只是对这些结构的引用(指针)。 CPython 中的基本类型结构称为PyObject,这个结构有一个ob_type slot,它告诉Python 什么是类型。类型只是more C structures

如果您想了解 CPython 源代码,您可以从 bltinmodule.c source code 开始(因为 type 是一个内置名称),即 defines type as the PyType_Type structure。调用一个typetype 也是一个type)调用他们的tp_new functionPyType_Typedefines that 作为the type_new function。此函数处理带有 one 参数的调用,如下所示:

/* 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 is a macro 简单地返回任何 PyObject 结构的 ob_type 值。

所以现在您有了1'foobar' 的类型对象;你怎么会在你的口译会话中看到&lt;class 'int'&gt;&lt;class 'str'&gt;? Python 交互式解释器自动对任何表达式结果使用repr() function。在 PyType_Type 定义的 C 结构中,PyType_Type 结构被合并,因此该类型的所有插槽都可以直接使用;我将在这里省略 that 的工作原理。对于类型对象,使用repr() 意味着调用type_repr function 并返回:

rtn = PyUnicode_FromFormat("<class '%s'>", type->tp_name);

所以最后,type(1) 获得了-&gt;ob_type 插槽(这在 Python 3 中是 PyLong_Type struct,长篇大论),并且该结构有一个 tp_name slot set to "int"

TL;DR:Python 变量没有类型,它们只是指向对象的指针。 对象具有类型,如果您在解释器中回显对象,Python 解释器将遵循一系列间接引用以到达要打印的类型名称。

【讨论】:

    【解决方案2】:

    Python 变量没有类型,它们只是对对象的引用。无论引用什么,引用的大小都是相同的。在 Python 的 C 实现中,它是一个指针,确实有一个类型,它是一个指向 Python 对象的指针:PyObject *。无论对象的类别如何,指针都是相同的类型。另一方面,对象知道它们属于哪个类。

    有人认为 Python 没有变量,只有名称,尽管这对大多数人来说太过分了。

    CPython 实现中的引用有一个 id(标识符),它实际上是一个虚拟地址。这个地址的细节和价值不值得追求——它可以(并且可能会)在版本之间改变,并且除了用于标识对象的唯一编号之外,不用于任何其他用途。然而,它可以为正在发生的事情提供有趣的指针(请原谅双关语):

    >>> x = 42
    >>> y = x
    >>> id(x)
    4297539264
    >>> id(y)
    4297539264
    

    请注意,xy 的 id(地址)是相同的——它们引用的是同一个对象,一个值为 42 的 int。所以,当我们更改 x 时会发生什么y也要改吗?

    >>> x = "hello"
    >>> id(x)
    4324832176
    >>> id(y)
    4297539264
    

    谢天谢地没有。现在x 只是指类str 的新对象,其值为“Hello”。

    当我们:

    >>> id(y)
    4297539264
    >>> y = 37
    >>> id(y)
    4297539104 
    

    y 的 id 改变了!这是因为它现在引用了不同的对象。 ints 是不可变的,所以赋值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 只复制了引用,而不是列表。请参阅标准库中的copy

    【讨论】:

      【解决方案3】:

      变量的“类型”概念是通过使用特定类的对象来“实现”的。

      所以在

      a=float()

      float 类定义的 float 类型的对象由 float() 返回。 Python 知道它是什么类型,因为这就是对象的工作方式:你知道它们是什么类型。 a 现在是 float 对象,值为 0.0。

      使用内置函数也是一样的,只是它们有声明它们的简写。

      i=123

      一样

      i=int(123)

      int() 返回一个整数类对象,值为 123。

      类似

      i="123"

      相同

      i=str("123")

      str("123")返回一个str类的对象,值为“123”

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-10-21
        • 1970-01-01
        • 2010-12-01
        • 2016-04-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多