首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >Python如何以及何时确定变量的数据类型?

Python如何以及何时确定变量的数据类型?
EN

Stack Overflow用户
提问于 2018-11-23 15:13:29
回答 3查看 2.8K关注 0票数 11

我试图弄清楚Python3(使用CPython作为解释器)是如何执行其程序的。我发现这些步骤是:

  1. 将Python源代码(.py文件)由CPython编译器编译为Python (.pyc)文件。在导入任何模块的情况下,保存.pyc文件,如果运行一个main.py Python脚本,则不会保存它们。
  2. Python虚拟机将字节码解释成硬件专用的机器代码。

这里发现的一个很好的答案是,与JVM相比,Python运行字节码所需的时间更长,因为java字节码包含有关数据类型的信息,而Python则逐一解释行并确定数据类型。

我的问题是是如何确定数据类型的,它是发生在对机器代码的解释中还是在单独的过程中(例如,这会产生另一个中间代码)?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2018-11-24 16:07:07

CPython的动态、运行时分派(与静态的、编译时分派的CPython相比)只是原因之一,它比纯CPython更快: Java中有jit编译、不同的垃圾收集策略、本地类型(如intdouble和CPython中的不可变数据结构)的存在等等。

我之前的肤浅实验已经表明,动态调度只负责大约30%的运行--您无法用它解释某些重要因素的速度差异。

为了减少这个答案的抽象性,让我们看一个例子:

代码语言:javascript
代码运行次数:0
运行
复制
def add(x,y):
   return x+y

查看字节码:

代码语言:javascript
代码运行次数:0
运行
复制
import dis
dis.dis(add)

这意味着:

代码语言:javascript
代码运行次数:0
运行
复制
2         0 LOAD_FAST                0 (x)
          2 LOAD_FAST                1 (y)
          4 BINARY_ADD
          6 RETURN_VALUE

我们可以看到,在字节码的级别上,无论xy是整数、浮点数还是其他什么,都没有区别--解释器并不在意。

Java中的情况完全不同:

代码语言:javascript
代码运行次数:0
运行
复制
int add(int x, int y) {return x+y;}

代码语言:javascript
代码运行次数:0
运行
复制
float add(float x, float y) {return x+y;}

将导致完全不同的操作码,调用分派将在编译时发生--根据编译时已知的静态类型选择正确的版本。

CPython-解释器通常不需要知道确切的参数类型:内部有一个基本的“类/接口”(显然C中没有类,所以它被称为“协议”,但是对于知道C++/Java的人来说,“接口”可能是正确的心理模型),所有其他“类”都是从这个模型派生出来的。这个基类称为PyObject这是对其协议的描述。。因此,只要函数是这个协议/接口的一部分,CPython解释器就可以调用它,而不知道确切的类型,调用将被分派到正确的实现(很像C++中的“虚拟”函数)。

在纯Python方面,似乎变量没有类型:

代码语言:javascript
代码运行次数:0
运行
复制
a=1
a="1"

但是,在内部a有一个类型--它是PyObject*,这个引用可以绑定到一个整数(1)和一个unicode-string ("1") --因为它们都是从PyObject“继承”的。

CPython解释器不时尝试查找引用的正确类型,上述示例也是如此--当它看到BINARY_ADD-opcode时,以下C代码将被执行:

代码语言:javascript
代码运行次数:0
运行
复制
    case TARGET(BINARY_ADD): {
        PyObject *right = POP();
        PyObject *left = TOP();
        PyObject *sum;
        ...
        if (PyUnicode_CheckExact(left) &&
                 PyUnicode_CheckExact(right)) {
            sum = unicode_concatenate(left, right, f, next_instr);
            /* unicode_concatenate consumed the ref to left */
        }
        else {
            sum = PyNumber_Add(left, right);
            Py_DECREF(left);
        }
        Py_DECREF(right);
        SET_TOP(sum);
        if (sum == NULL)
            goto error;
        DISPATCH();
    }

在这里,解释器查询两个对象是否都是unicode字符串,如果是这样的话,使用一种特殊的方法(实际上,它试图改变不变的unicode-object就地,请参阅此回答),否则工作将被分派到PyNumber-protocol。

显然,解释器还必须知道创建对象的确切类型,例如,对于a="1"a=1使用不同的“类”--但正如我们所看到的,它并不是唯一的位置。

因此,解释器在运行时会干扰类型,但大多数情况下,它不必这样做--可以通过动态分派来达到目标。

票数 4
EN

Stack Overflow用户

发布于 2018-11-26 00:12:53

避免在Python中考虑“变量”可能对您的理解很有用。与必须将类型与变量、类成员或函数参数相关联的静态类型化语言相比,Python只处理对象的“标签”或名称。

所以在片段里,

代码语言:javascript
代码运行次数:0
运行
复制
a = "a string"
a = 5 # a number
a = MyClass() # an object of type MyClass

标签a从来没有类型。它只是一个在不同时间指向不同对象的名称(实际上,在其他语言中指向“指针”非常类似)。另一方面,对象(字符串、数字)总是有一个类型。这种类型的性质可能会改变,因为您可以动态地更改类的定义,但它将始终被确定,即由语言解释器知道。

因此,要回答这个问题: Python从不确定变量的类型(标签/名称),它只使用它来引用对象,而该对象有一个类型。

票数 2
EN

Stack Overflow用户

发布于 2018-11-23 16:02:22

Python是围绕鸭类输入的哲学构建的。不进行显式类型检查,即使在运行时也不会发生。例如,

代码语言:javascript
代码运行次数:0
运行
复制
>>> x = 5
>>> y = "5"
>>> '__mul__' in dir(x)
>>> True
>>> '__mul__' in dir(y)
>>> True
>>> type(x)
>>> <class 'int'>
>>> type(y)
>>> <class 'str'>
>>> type(x*y)
>>> <class 'str'>

CPython解释器检查xy是否定义了__mul__方法,并尝试“使其工作”并返回结果。而且,Python字节码从未被翻译成机器代码。它在CPython解释器中执行。JVM和CPython虚拟机之间的一个主要区别是,JVM可以随时将Java字节码编译成机器代码,以提高性能(JIT编译),而CPython VM只按原样运行字节码。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53449112

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档