我试图弄清楚Python3(使用CPython作为解释器)是如何执行其程序的。我发现这些步骤是:
这里发现的一个很好的答案是,与JVM相比,Python运行字节码所需的时间更长,因为java字节码包含有关数据类型的信息,而Python则逐一解释行并确定数据类型。
我的问题是是如何确定数据类型的,它是发生在对机器代码的解释中还是在单独的过程中(例如,这会产生另一个中间代码)?
发布于 2018-11-24 08:07:07
CPython的动态、运行时分派(与静态的、编译时分派的CPython相比)只是原因之一,它比纯CPython更快: Java中有jit编译、不同的垃圾收集策略、本地类型(如int
、double
和CPython中的不可变数据结构)的存在等等。
我之前的肤浅实验已经表明,动态调度只负责大约30%的运行--您无法用它解释某些重要因素的速度差异。
为了减少这个答案的抽象性,让我们看一个例子:
def add(x,y):
return x+y
查看字节码:
import dis
dis.dis(add)
这意味着:
2 0 LOAD_FAST 0 (x)
2 LOAD_FAST 1 (y)
4 BINARY_ADD
6 RETURN_VALUE
我们可以看到,在字节码的级别上,无论x
和y
是整数、浮点数还是其他什么,都没有区别--解释器并不在意。
Java中的情况完全不同:
int add(int x, int y) {return x+y;}
和
float add(float x, float y) {return x+y;}
将导致完全不同的操作码,调用分派将在编译时发生--根据编译时已知的静态类型选择正确的版本。
CPython-解释器通常不需要知道确切的参数类型:内部有一个基本的“类/接口”(显然C中没有类,所以它被称为“协议”,但是对于知道C++/Java的人来说,“接口”可能是正确的心理模型),所有其他“类”都是从这个模型派生出来的。这个基类称为PyObject
和这是对其协议的描述。。因此,只要函数是这个协议/接口的一部分,CPython解释器就可以调用它,而不知道确切的类型,调用将被分派到正确的实现(很像C++中的“虚拟”函数)。
在纯Python方面,似乎变量没有类型:
a=1
a="1"
但是,在内部a
有一个类型--它是PyObject*
,这个引用可以绑定到一个整数(1
)和一个unicode-string ("1"
) --因为它们都是从PyObject
“继承”的。
CPython解释器不时尝试查找引用的正确类型,上述示例也是如此--当它看到BINARY_ADD
-opcode时,以下C代码将被执行:
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
使用不同的“类”--但正如我们所看到的,它并不是唯一的位置。
因此,解释器在运行时会干扰类型,但大多数情况下,它不必这样做--可以通过动态分派来达到目标。
发布于 2018-11-25 16:12:53
避免在Python中考虑“变量”可能对您的理解很有用。与必须将类型与变量、类成员或函数参数相关联的静态类型化语言相比,Python只处理对象的“标签”或名称。
所以在片段里,
a = "a string"
a = 5 # a number
a = MyClass() # an object of type MyClass
标签a
从来没有类型。它只是一个在不同时间指向不同对象的名称(实际上,在其他语言中指向“指针”非常类似)。另一方面,对象(字符串、数字)总是有一个类型。这种类型的性质可能会改变,因为您可以动态地更改类的定义,但它将始终被确定,即由语言解释器知道。
因此,要回答这个问题: Python从不确定变量的类型(标签/名称),它只使用它来引用对象,而该对象有一个类型。
发布于 2018-11-23 08:02:22
Python是围绕鸭类输入的哲学构建的。不进行显式类型检查,即使在运行时也不会发生。例如,
>>> 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解释器检查x
和y
是否定义了__mul__
方法,并尝试“使其工作”并返回结果。而且,Python字节码从未被翻译成机器代码。它在CPython解释器中执行。JVM和CPython虚拟机之间的一个主要区别是,JVM可以随时将Java字节码编译成机器代码,以提高性能(JIT编译),而CPython VM只按原样运行字节码。
https://stackoverflow.com/questions/53449112
复制相似问题