Python 子类继承

5 投票
5 回答
13542 浏览
提问于 2025-04-16 11:57

我正在尝试创建一些类,这些类是从一个父类继承而来的,而这个父类又包含一些从其他父类继承的子类。但是,当我在任何子类中更改属性时,这个更改会影响到所有的子类。我希望避免创建实例,因为我在后面会用到这个功能。

下面的代码简化了这个问题。最后一行显示了意想不到的结果。

class SubclsParent(object):
    a = "Hello"

class Parent(object):
    class Subcls(SubclsParent):
        pass

class Child1(Parent):
    pass

class Child2(Parent):
    pass

Child1.Subcls.a # Returns "Hello"
Child2.Subcls.a # Returns "Hello"
Child1.Subcls.a = "Goodbye"
Child1.Subcls.a # Returns "Goodbye"
Child2.Subcls.a # Returns "Goodbye" / Should still return "Hello"!

相关问题:

5 个回答

1

当你使用 Child1.Subcls 时,Python 会发现 Child1.Subcls 并不存在,于是它去查找 Parent,结果找到了并返回了它。Child2.Subcls 也是同样的情况。因此,这两个表达式实际上指向的是同一个类。Child1 和 Child2 并没有各自独立的子类,而是共享了原来的那个类。

*我希望避免创建实例,因为我后面会用到这个功能。*

我不太明白你这句话的意思。

3

试试这个

class SubclsParent(object):
    def __init__(self):
         self.a = "Hello"

当你直接在类里面定义 SubclsParent.a 的时候,你是在把它定义为静态的。

10

你看到的行为正是你应该预期的。当你定义一个类时

>>> class Foo(object): pass
...

你可以修改这个类——不是它的实例,而是类本身——因为类其实就是另一个对象,存储在变量Foo中。所以,比如说,你可以获取和设置这个类的属性:

>>> Foo.a = 1
>>> Foo.a
1

换句话说,class这个关键词创建了一种新的对象类型,并将指定的名称绑定到这个对象上。


现在,如果你在另一个类里面定义一个类(顺便说一下,这种做法有点奇怪),这就相当于在类的主体内部定义一个局部变量。你知道,在类的主体内部定义局部变量会发生什么:它们会被设置为类属性。换句话说,局部定义的变量是存储在类对象上的,而不是存储在每个实例上。因此,

>>> class Foo(object):
...     class Bar(object): pass
...

定义了一个类Foo,它有一个类属性Bar,而Bar本身也是一个类。不过,这里并没有发生子类化——类FooBar是完全独立的。(你实现的行为可以通过以下方式复制:

>>> class Foo(object):
...     class Bar(object): pass
...
>>> class Foo(object): pass
...
>>> class Bar(object): pass
...
>>> Foo.Bar = Bar

。)

所以你总是在修改同一个变量!当然你看到的值会改变;因为你自己已经改变了它们!


你的问题似乎是你对实例属性和类属性有些混淆,它们其实是完全不同的东西。

属性是一个在整个类中定义的变量。也就是说,类的任何实例都会共享这个变量。例如,大多数方法都是类属性,因为你通常希望在每个实例上调用相同的方法。你也可以用类属性来做一些全局计数器(比如你实例化了这个类多少次?)和其他应该在实例之间共享的属性。

实例属性是一个特定于类实例的变量。也就是说,每个实例都有这个变量的不同副本,内容可能也不同。这就是你在类中存储数据的地方——如果你有一个Page类,你会希望contents属性是按实例存储的,因为不同的Page当然需要不同的contents

在你的例子中,你希望Child1.Subcls.aChild2.Subcls.a不同的变量。自然,它们应该依赖于实例!


这可能有点跳跃,但你是在尝试在Python中实现Java风格的接口吗?换句话说,你是在试图指定一个类应该具备哪些属性和方法,而不实际定义这些属性吗?

以前,这被认为是一种不太符合Python风格的做法,因为普遍的共识是你应该允许类随心所欲地做事,并在它们没有定义所需的属性或方法时捕获异常。然而,最近人们意识到接口有时其实是个好东西,因此Python增加了新功能来支持这一点:抽象基类

撰写回答