Python:如何继承和重写

15 投票
3 回答
18823 浏览
提问于 2025-04-15 22:45

考虑这样一种情况:

我有一个类型为 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 个回答

2

在Python中,对于实例方法,你需要把self这个参数放到参数列表里。

一旦你这样做了,它就会正常工作,因为在Python中,所有的方法都是虚拟的。

5

试试这个:

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
11

你要创建一个子类 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 和它的实例是“比较普通的”,那么我强烈建议选择简单和清晰的方式。

撰写回答