Python: 从派生类获取基类值

17 投票
2 回答
41397 浏览
提问于 2025-04-16 11:27

希望这个解释足够清楚:

class myParent():
    def __init__( self ):
        self.parentNumber = 5

class Child( myParent ):
    def __init__( self ):
        self.childNumber = 4

    def multiplyNumbers( self ):
        print myParent.parentNumber * self.childNumber

p = Child()
p.multiplyNumbers()

我想单独设置父类的数字,然后通过子类来获取这个数字,并用它进行一些乘法运算。

我刚接触面向对象编程(OOP),所以如果你能给我一些关于继承的基本建议,我会很感激!

更多信息: 我正在为基于视觉特效的项目设计一个项目管理解决方案,正在尝试使用类和继承,看看它们能给我带来什么帮助。

现在,我有一个顶层类,叫做Project,还有一个派生类,叫做Shot。Shot类有一个self.length变量,表示特定镜头的长度。它还有一个getLengthInSeconds()方法,这个方法使用self.length和Project.fps来计算长度(以秒为单位)。Project类有一个setFps()方法,用于在创建类的实例后设置fps。

我习惯于变量前面加上self.,而且没有太多尝试过不带self.的“全局”变量。如果我把所有变量都设为全局的,不用self.,我可以轻松使用Project.fps,但我心里总觉得这样有点“坏编程习惯”。也许有更好、更整洁的方法?

编辑:

经过一些阅读,我觉得super()有点危险,而且可能超出了我的需求。我主要使用的是单继承类,甚至不太确定如何利用菱形继承结构……有没有更安全的方法来访问父类的变量和方法,而不需要用到super()?

编辑:

好吧,看看这样是否有道理,或者我是不是想错了。

我把类和继承看作是父母和孩子。一个孩子知道它的父母和所有的属性。另一个父母的孩子知道那个父母的属性。我想要实现的是让所有创建的镜头都属于一个项目。现在,我是在Project()类中创建Shot()实例,并把这些实例添加到一个镜头列表中,这个列表会在Project()实例中维护。

也就是说:

class myParent( object ):
    def __init__( self ):
        self.parent_id = ''
        self.children = []

    def createChild( self, name ):
        self.children.append( myChild( name ) )

    def getChildren( self ):
        return self.children

    def setParentId( self, id ):
        self.parentId = id

class myChild( myParent ):
    def __init__( self, id ):
        super(myChild, self).__init__()
        self.id = id

    def getParentId( self ):
        return self.parent_id

p = myParent()
p.setParentId( 'parent01' )
p.createChild( 'child01' )
print p.getChildren()[0].getParentId()

我能看到这里逻辑上的一些错误,但没有真正的解决办法……看起来每个孩子都在以这种方式获得一个新的父类实例,而parent_id总是一个空字符串。

2 个回答

6

你只需要告诉 Child 去运行 myParent 的初始化代码。这样,当前的实例(self)就会拥有 myParent 实例所拥有的所有属性(这就是所谓的“是一个”的关系),所以你可以使用 self.parentNumber。你可以通过在 Child.__init__ 中加入 super(Child, self).__init__() 来实现(最好放在第一行)。虽然 myParent.__init__(self) 也能工作,但在基类发生变化或使用多重继承时可能会出错。使用 Python 3 的话,你可以把 super 的所有参数都省略。

另外要注意的是

class C(object):
    x = ...

class C(object):
    def __init__(self):
        self.x = ...

是非常不同的。前者创建了一个类变量,这个变量是所有 C 的实例共享的。而后者创建了一个实例变量,每个 C 的实例都有自己的这个变量。(同样,全球变量并不绑定到实例上——但我猜你是在说上面的内容?)

13
class myParent( object ):
    def __init__( self ):
        self.parentNumber = 5

class Child( myParent ):
    def __init__( self ):
        myParent.__init__( self )
        self.childNumber = 4

    def multiplyNumbers( self ):
        print self.parentNumber * self.childNumber

p = Child()
p.multiplyNumbers()

通常情况下,如果你没有使用多重继承或者其他特殊情况,使用super是没问题的。不过,如果父类发生了变化,你需要记得在init(以及其他任何明确提到myParent的方法)中更改父类的名称。

如果你父类的__init__方法需要参数,你就需要在子类的__init__中把这些参数传递过去。

class myParent( object ):
    def __init__( self, customParam ):
        self.parentNumber = 5
        self.customParam = customParam

class Child( myParent ):
    def __init__( self, customParam ):
        myParent.__init__( self, customParam )
        self.childNumber = 4

如果你不喜欢在所有子类中重复写customParam,可以考虑一种叫做“两阶段构造”的面向对象模式,工作原理如下:

class myParent( object ):
    def customInit( self, customParam ):
        self.parentNumber = 5
        self.customParam = customParam

class Child( myParent ):
    def __init__( self, customParam ):
        self.childNumber = 4

p = Child()
p.customInit(10)
p.multiplyNumbers()

在这种模式下,你不需要在子类中重复父类的参数,甚至不需要在子类中调用父类的__init__,但缺点是你需要记得在创建对象时始终调用第二个构造函数,否则你的对象会处于部分未初始化的状态。

更新(为了回答更新后的问题):

你似乎把两个不相关的概念搞混了。继承是关于类型层次结构的,而你似乎是在讨论一种拥有层次结构(is-a与has-a)。

我会把你更新后的代码结构调整成这样:

class myParent( object ):
    def __init__( self, parentId ):
        self.id = parentId
        self.children = []

    def createChild( self, name ):
        self.children.append( myChild( name, self ) )

    def getChildren( self ):
        return self.children

class myChild( object ):
    def __init__( self, childId, parent ):
        self.id = childId
        self.parent = parent

    def getParentId( self ):
        return self.parent.id

p = myParent( 'parent01' )
p.createChild( 'child01' )
print p.getChildren()[0].getParentId()

撰写回答