Python:如何继承和重写
考虑这样一种情况:
我有一个类型为 A
的对象,这个对象里面有一个函数 f
:
class A:
def f(self):
print 'in f'
def h(self):
print 'in h'
我得到了这个类的一个实例,但我想要重写这个 f
函数,同时又想保留 A
的其他功能。所以我在想,可能可以这样做:
class B(A):
def __init__(self, a):
#something here
....
def f(self):
print 'in B->f'
然后使用的时候会是:
def main(a):
b = B(a)
b.f() #prints "in B->f"
b.h() #print "in h"
我想要的是一种复制构造函数,它可以获取当前类的父类(A
),并返回这个类的一个实例(B
)。
那么该怎么做呢?__init__
方法应该是什么样子的?
注意:这篇帖子已经被原作者编辑过,以包含评论中建议的更改,这就是为什么有些建议看起来重复或不正确。
3 个回答
在Python中,对于实例方法,你需要把self
这个参数放到参数列表里。
一旦你这样做了,它就会正常工作,因为在Python中,所有的方法都是虚拟的。
试试这个:
class A:
def f(self):
print("in f")
def h(self):
print("in h")
class B(A):
def f(self):
print("in B:f")
def test(x):
x.f()
x.h()
test(A())
test(B())
注意,我使用的是Python 3,这就是为什么print
函数的参数要放在括号里的原因。
输出结果:
in f
in h
in B:f
in h
你要创建一个子类 B
的对象,主要看父类 A
是怎么管理它的状态的。如果 A
没有状态,那在 B
的 '__init__'
方法里就不需要做任何事情。在你的例子里,A
的实例是无状态的,所以 B
的初始化就很简单。
class A(object):
def __init__(self):
self._x = 23
self._y = 45
def f(self):
print 'in f,', self._x
def h(self):
print 'in h,', self._y
在更常见的例子中,状态可能会保存在两个实例属性 _x
和 _y
中,这就是你需要复制的内容:
class B(A):
def __init__(self, a):
self._x = a._x
self._y = a._y
def f(self):
print 'in B->f,', self._x
这是最常见的做法,子类直接接受并实现对父类状态的依赖,过程非常简单明了。
通常你会在 A
的 '__init__'
方法里寻找实例状态,因为大多数正常的 Python 代码在初始化时就会设置实例状态(属性可能会在后面添加或删除,甚至在类体外的代码中修改,但这并不常见,也不太建议这样做)。
当然,也可以加入一些“魔法”(基于反射的编程),比如...:
class B1(A):
def __init__(self, a):
try: s = a.__getstate__()
except AttributeError: s = a.__dict__
try: self.__setstate__(s)
except AttributeError: self.__dict__.update(s)
getstate 是一个特殊的方法,类可以定义这个方法。如果定义了,它会在序列化时被用来“获取”实例的状态(否则,实例的 __dict__
就被认为是实例的“状态”)。这个方法可以返回一个字典(这样 .update
调用就会更新 self
的状态),但如果类还定义了一个接受其他返回值的 __setstate__
方法,它也可以返回其他类型的值(所以这段代码会先尝试这个方法,再回退到更新的方式)。需要注意的是,在这种情况下,这两个特殊方法可能会从 A
继承过来,我不建议在 B
中重新定义它们,除非有其他更复杂的需求。
用这四行“魔法”代替我最开始建议的简单赋值值得吗?大多数情况下,不值得——简单更好。但如果 A
做了什么特别的事情,或者有外部代码会改变它的状态,这种方法可能会更强大和通用(这就是你接受复杂性的原因)。所以,你需要判断是哪种情况:如果是后者,就可以使用这些特殊的状态相关方法;如果 A
和它的实例是“比较普通的”,那么我强烈建议选择简单和清晰的方式。