fo = open("PY301-2.txt","w") class Horse(): def __init__(self, category, gender, age): self.category = category self.gender = gender self.age = age def get_descriptive(self): self.info = "一匹" + self.category + str(self.age) + "岁的" + self.gender + "马" def write_speed(self, new_speed): self.speed = new_speed addr = "在草原上奔跑的速度为" fo.write(self.info + "," + addr + str(self.speed) + "km/h。") class Camel(Horse): def __init__(self, category, gender, age): super().__init__(category, gender, age) def write_speed(self,new_speed): self.speed = new_speed addr = "在沙漠上奔跑的速度为" fo.write(self.info.replace("马","骆驼") + "," + addr + str(self.speed) + "km/h。") camel = Camel("双峰驼","母",20) camel.write_speed(40) fo.close() 报没有info属性异常
实例属性”不是通过继承机制向下传,而是在对象创建后被动态地挂在每个实例自己身上(self.__dict__
或 __slots__
)。
__init__
里)class A: def __init__(self, x): self.x = x # 给实例挂属性 class B(A): pass b = B(10) print(b.x) # 10 —— 子类实例直接拿到了
B()
里找不到 __init__
,沿 MRO 找到 A.__init__
;A.__init__
把 self.x = x
挂在 当前实例 b 上;b.x
存在——不是“继承”来的,而是父类方法运行时顺手绑上去的。__init__
却忘了调超类,属性就不会出现:class C(A): def __init__(self): # 完全覆盖,没调用 super pass c = C() # print(c.x) -> AttributeError
__init__
里统一绑定,并让子类显式 super().__init__()
super.字段名
直接访问。super.字段名
访问父类那份。class Parent { int x = 10; // 非 private } class Child extends Parent { int x = 20; // 隐藏父类 x void show() { System.out.println(super.x); // 10 System.out.println(this.x); // 20 } }
private
修饰的字段不会继承到子类命名空间;public/protected
方法间接读写。super(...)
,保证父类字段先被初始化;super(...)
先完成父类初始化——这些规则都与 Python 的“动态挂属性”机制完全不同。
Python 在字节码层面不会把父类的字段复制一份到子类里。
Java 那种“子类字节码里就带着父类所有非私有字段”的做法是 Java 虚拟机为了实现快速实例布局而做的编译期/加载期优化,属于静态语言的实现细节;Python 的对象模型完全是另一套——动态、基于字典(或隐藏类优化)的运行时查找机制,字段到底存在哪里、叫什么都得在运行时一步步算出来。
下面把两件事分开说清楚:
1. 类对象(class 本身)里存了什么
2. 实例对象(instance)里存了什么

1. 类对象层面
• 任何 Python 类都有一个 __bases__ 元组,按 MRO(方法解析顺序)存放它的直接基类。
• 类自己的属性命名空间放在 __dict__ 里(只放“本层”定义的,不会把父类的键值复制进来)。
• 查找属性时,解释器会沿着 MRO 链逐个去对应类的 __dict 里找,找到即返回。
因此父类字段在子类里只有一份,子类并没有在字节码或内存里复制它们。

2. 实例对象层面
• 每个实例有一个 __dict__(除非类禁用了 __dict__ 而用 __slots__)。
• 实例只会保存“在本层 __init__ 里真正赋过值”的属性。
如果子类 __init__ 里没给某个父类字段赋值,那么即使父类有默认值,该实例的 __dict__ 里也不会出现这个键;后续访问时解释器沿 MRO 查到类属性再返回。
• 因此父类字段不会“预装”到子类实例,更谈不上字节码里带一份。
class A: x = 1 # 类属性 def __init__(self): self.y = 2 # 实例属性 class B(A): def __init__(self): super().__init__() # 这里才把 self.y 真正写进实例 dict self.z = 3 b = B() print(b.__dict__) # {'y': 2, 'z': 3} print(B.__dict__) # 只有 'z'、'__init__' 等本层定义,没有 'x' print(b.x) # 1 —— 运行时在 A.__dict__ 里找到 • B 的字典里根本没有 x,实例 b 的字典里也没有 x;访问 b.x 是运行时沿 MRO 查到的。 • 如果 B 的 __init__ 不调用 super(),那么实例甚至不会有 y。  一句话总结 Java 为了静态布局性能,在编译/类加载阶段就把父类字段“摊平”到子类; Python 保持动态语义,父类字段只在运行期按需查找,子类字节码和实例字典里都不会提前复制一份。