首页 新闻 会员 周边 捐助

Python 在语义上是 100 % 纯面向对象,Java 在语义上是“混合模型”

0
[已关闭问题] 关闭于 2025-08-30 18:58
  • Python 在语义上是 100 % 纯面向对象
    • 一切皆对象(包括字面量 42Truelen 本身)。
    • 所有对象都有身份(id)、类型(type)、值(value),都能接收消息(方法调用)。
    • 没有“基本类型 vs 对象”的区分,因此变量只是“名字→对象”的绑定。
      • Java 在语义上是“混合模型”
        • 保留 int, double, boolean非对象 的原始类型(primitive)。
        • 这些原始类型没有方法、没有继承、不能放进 Object[],必须用装箱类(Integer, Double)才能享受对象待遇。
        • 因此 Java 无法宣称“一切皆为对象”——这就是它“不完全面向对象”的典型证据。

      实现层面

      • Java 把基本类型的值直接放在栈帧里,是为了性能;
      • Python 把所有对象放在堆里,只是实现策略,并不影响“一切皆对象”的语义声明。
_java_python的主页 _java_python | 小虾三级 | 园豆:984
提问于:2025-08-30 18:45
< >
分享
所有回答(1)
0

 

  1. 为什么要有“统一的最小结构”?
  • Python 里一切皆为对象:整数 42、列表 [1,2]、函数 len、类 int 本身……
  • C 是静态类型,必须提前知道“这块内存有多大、里面有什么”。
  • 于是 CPython 定义了最小可识别头 PyObject(含引用计数 + 类型指针),后面爱加多少字段再加。
    → 任何对象都能被当作 PyObject * 安全地传递。

  1. 为什么引用计数要放在 PyObject 里?
  • CPython 用引用计数做垃圾回收。
  1. 为什么类型信息也放在 PyObject 里?
  • Python 是动态类型,type(x) 必须能在运行时回答。
  • ob_type 指针塞进头部,解释器就能:
    ‑ 找到对象的类(ob_type
    ‑ 找到类的方法表(ob_type->tp_methods 等)
    ‑ 实现多态调用(obj->ob_type->tp_repr(obj)
  • PyObject 就是 CPython 的“对象通行证”:少了它,解释器连怎么分配、怎么回收、怎么找方法都无从下手。

CPython 源码中,PyObject * 是一个指向对象结构体的指针,它是 Python 所有对象的“最小公共祖先”:

typedef struct _object {
Py_ssize_t ob_refcnt; // 引用计数
struct _typeobject *ob_type; // 类型指针
} PyObject;   外只暴露 PyObject 这个名字,隐藏底层 struct _object,struct _object 是结构体的标签名(tag)只在结构体内部可见 

  • PyObject 是类型别名(typedef 名),对外使用。  使用时直接写 PyObject obj;不用写struct _object obj
这个结构体非常小,只包含两个字段:
  • ob_refcnt:引用计数,用于垃圾回收。
  • ob_type:指向类型对象的指针(也就是你熟悉的 Python 的 type(obj))。

继承 PyObject 的对象长啥样?

比如一个自定义对象:
c

typedef struct {
    PyObject_HEAD  // 展开后是 PyObject 的成员
    int my_value;
} MyObject;
  • MyObject 的第一个成员就是 PyObject,所以你可以把 MyObject* 当作 PyObject* 来用。
在 C 语言里谈“继承”,本质上就是把父结构体完整地嵌(放)在子结构体的最前面,除此之外没有别的魔法—没有 extends,没有自动向上转型,也没有虚函数表,全靠“内存布局”+“强制类型转换”来手动实现。C 语言本身并没有“继承、多态”这两个语法概念
把“Python 变量”落到 CPython 的内存里,可以浓缩成三句话:
  1. 所有“真正的对象”都是一块堆上的结构体,这块结构体的第一个字段一定是 PyObject(或变长版 PyVarObject
    所以“继承”体现在 C 层面就是:
    对象内存布局 = [ PyObject 头部 | 子类私有字段 ]
     
  2. 名字空间里只存“一级指针”——PyObject *,不是二级指针
    • 全局名字空间:对应 PyDictObject,键是字符串,值是 PyObject *
    • 局部名字空间:CPython 3.11 之前是 PyDictObject,3.11 起有了更紧凑的 PyLocalsArray,但同样存的是 PyObject *
    • 闭包、类属性、实例 __dict__ 同理,值都是 8 字节(64 位机)指针。
  3. 变量名只是字典里的键,给这块指针起了个人类可读的“标签”
    locals["lst"] -----> PyListObject 在堆上的地址
     
    变量名本身不额外占“二级”间接层;真正多出来的一跳来自 “名字空间 → 对象” 这一步。
Python 层:      lst = [1, 2, 3]

C 层:
    locals (PyDictObject)
    +---------+
    | key     | value (PyObject *)
    | "lst"   | 0x7f8e3c2a1b00  ----------> [PyObject_HEAD | ob_size=3 | *ob_item]
    +---------+
没有 PyObject **;
没有“二级指针”;
变量名 "lst" 只是字典键,指向唯一的 PyListObject。

“所有对象都继承自 PyObject,变量名只是给指向堆对象的指针贴的标签。

PyListObject 里面又是一块连续的“一级指针”数组,专门用来存放列表元素
_java_python | 园豆:984 (小虾三级) | 2025-08-30 18:58
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册