Python: 从派生类获取基类值
希望这个解释足够清楚:
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 个回答
你只需要告诉 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
的实例都有自己的这个变量。(同样,全球变量并不绑定到实例上——但我猜你是在说上面的内容?)
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()