首页 新闻 会员 周边 捐助

实例属性能继承?

0
[已解决问题] 解决于 2025-09-07 16:41
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__)。

  • 子类不会自动得到父类曾经赋过的那些实例属性值;
  • 但子类实例完全能访问到父类代码里给 self 绑定的任何属性,只要父类的方法曾经运行过(最常见就是 __init__ 里)
  • class A:
        def __init__(self, x):
            self.x = x          # 给实例挂属性
    
    class B(A):
        pass
    
    b = B(10)
    print(b.x)      # 10  —— 子类实例直接拿到了
    表面好像“继承”了,其实是:
    1. B() 里找不到 __init__,沿 MRO 找到 A.__init__
    2. A.__init__self.x = x 挂在 当前实例 b 上;
    3. 因此 b.x 存在——不是“继承”来的,而是父类方法运行时顺手绑上去的。
    如果父类方法没被执行,或者子类自己重写了 __init__ 却忘了调超类,属性就不会出现:
  • class C(A):
        def __init__(self):      # 完全覆盖,没调用 super
            pass
    
    c = C()
    # print(c.x)  -> AttributeError
    • 实例属性不存在“类级别”的继承表;
    • 只要父类代码有机会给 self 绑属性,子类实例就能访问到;
    • 想确保属性一定存在,常见做法是在父类 __init__ 里统一绑定,并让子类显式 super().__init__()
_java_python的主页 _java_python | 小虾三级 | 园豆:984
提问于:2025-09-07 15:47
< >
分享
最佳答案
0
Java 把“继承”做到字节码层面:子类对象真正拥有父类声明的所有字段(field),而且子类里可以用 super.字段名 直接访问。
与 Python 的“动态挂属性”相比,区别主要体现在下面几点:

  1. 字段(实例变量)的继承方式
    • 父类中非 private 的实例字段,子类自动继承一份;
    • 子类对象内存布局里紧挨着存放父类字段,再排自己的字段;
    • 如果父子出现同名字段,则**隐藏(hide)**而非覆写——子类对象里实际存了两份,用 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
          }
      }
      1. 私有字段
        • private 修饰的字段不会继承到子类命名空间;
        • 但子类对象仍然包含这块内存,只是不能直接访问;只能通过父类提供的 public/protected 方法间接读写。
      2. 静态字段
        • 属于类本身,不随继承重复拷贝;
        • 子类只是共享父类静态字段,除非自己再声明一个同名静态字段,此时也是隐藏关系。
      3. 构造器与初始化
        • 子类构造器第一行必须(显式或隐式)调用父类构造器 super(...),保证父类字段先被初始化;
        • 否则编译失败,这一点比 Python 严格得多。
        • Java 子类字节码层面就带有父类所有非私有字段,真正“继承”了属性;
          同名时隐藏、私有字段不可见、必须通过 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 保持动态语义,父类字段只在运行期按需查找,子类字节码和实例字典里都不会提前复制一份。

             

_java_python | 小虾三级 |园豆:984 | 2025-09-07 16:40
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册