Python 从父类控制子类方法

Python 从父类控制子类方法,python,class,python-2.7,parent-child,Python,Class,Python 2.7,Parent Child,假设我有以下代码: class Foo: def write(self, s=""): # Make sure that overwritten # 'write' method in child class # does what it's specified, and # then what comes next... print "-From Foo" class Bar(Foo): de

假设我有以下代码:

class Foo:
    def write(self, s=""):

        # Make sure that overwritten
        # 'write' method in child class
        # does what it's specified, and
        # then what comes next...

        print "-From Foo"

class Bar(Foo):
    def write(self, s=""):
        print s

baz = Bar()
baz.write("Hello, World!")
最后一个调用显然会自动输出hello world。我还需要让它编写“-From Foo”,但不修改Bar类,只修改Foo类。我尝试过使用
\uuu base\uuuu
和其他东西,但它对我的目的无效。

如果不修改
Bar()
,没有(好的)方法可以做到这一点-您想做的是在
Bar()
内部使用
super()
,这将允许您调用父方法

如果您使用的是一个不能修改的类,而这个类不能做到这一点,那么最好的解决方案是使用一个不太好的类来创建一个包装类,手动完成您想要的任务。例如:

class BarWrapper(Foo):
    def __init__(self, *args, **kwargs):
        self.bar = Bar(*args, **kwargs)

    def write(self, *args, **kwargs):
        super(BarWrapper, self).write(*args, **kwargs)
        self.bar.write(*args, **kwargs)

(当然,根据类的大小,还需要更多,注意在3.x中,您可以通过删除参数来使用
super()
的更简单语法。)

我100%同意Lattyware:您不应该这样做。父类不应该“知道”子类或它们是如何工作的

但我必须说,使用一些
\uuu getattribute\uuu
魔法是可能的:

class Foo(object):
    def __getattribute__(self, attr):
        if attr != 'write':
            return super(Foo, self).__getattribute__(attr)
        meth = super(Foo, self).__getattribute__(attr)
        if meth.im_func is Foo.write.im_func:
            # subclass does not override the method
            return meth

        def assure_calls_base_class(*args, **kwargs):
            meth(*args, **kwargs)
            Foo.write(self, *args, **kwargs)
        return assure_calls_base_class

    def write(self, s=""):
        print "-From Foo"



class Bar(Foo):
    def write(self, s=""):
        print s
运行代码:

>>> b = Bar()
>>> b.write('Hello, World!')
Hello, World!
-From Foo
但是请注意,这只是一种黑客行为,如果使用一点继承,或者即使您从类中访问
write
,也可能会中断:

>>> Bar.write(b, 'Hello, World!')  #should be equivalent to b.write('Hello, World!')
Hello, World!

这是一种使用元类魔法的方法;IMHO,它比其他方法更健壮和灵活,它还可以正确地处理无界调用(例如,
Bar.write(x,“hello”)
)和单个继承(参见下面的Baz):

控制台运行示例:

>>> class Foo(object):
...     __metaclass__ = ReverserMetaclass
...     def write(self, s=""):
...         # Make sure that overwritten
...         # 'write' method in child class
...         # does what it's specified, and
...         # then what comes next...
...         print "Write - From Foo", s
...     def read(self):
...         print "Read - From Foo"
...
>>> class Bar(Foo):
...     def write(self, s=""):
...         print "Write - from Bar", s
...     def read(self):
...         print "Read - From Bar"
...
>>> class Baz(Bar):
...     def write(self, s=""):
...         print "Write - from Baz", s
...
>>> x = Bar()
>>> x.write("hello")
Write - From Foo hello
Write - from Bar hello
>>> Bar.read(x)
Read - From Foo
Read - From Bar
>>>
>>> x = Baz()
>>> x.read()
Read - From Foo
Read - From Bar
>>> x.write("foo")
Write - From Foo foo
Write - from Bar foo
Write - from Baz foo

Python元类功能非常强大,但正如其他人所说,您确实不想经常使用这种魔法。

这里有另一种使用元类的方法。与使用
\uuu getattribute\uuu()
相比,它的一个重要优点是,访问或使用其他子类属性和方法不会产生额外的开销。如果定义了它的子类,它还支持单继承

class Foo(object):
    class __metaclass__(type):
        def __new__(metaclass, classname, bases, classdict):
            clsobj = super(metaclass, metaclass).__new__(metaclass, classname, 
                                                         bases, classdict)
            if classname != 'Foo' and 'write' in classdict:  # subclass?
                def call_base_write_after(self, *args, **kwargs):
                    classdict['write'](self, *args, **kwargs)
                    Foo.write(self, *args, **kwargs)

                setattr(clsobj, 'write', call_base_write_after)  # replace method

            return clsobj

    def write(self, s=""):
        print "-From Foo"

class Bar(Foo):
    def write(self, s=""):
        print 'Bar:', s

class Baz(Bar):  # sub-subclass
    def write(self, s=""):
        print 'Baz:', s

Bar().write('test')
Baz().write('test')
输出:

Bar:测试
-来自富
Baz:测试
-来自富
如果希望子类
write()
方法在以后调用基类的版本而不是根(
Foo
)类,只需更改硬编码:

    Foo.write(self, *args, **kwargs)
呼吁:

    super(clsobj, self).write(*args, **kwargs)

Foo.\uuuu new\uuuuu()

中,如果这是Python 2.x,请确保从object继承以获得新样式的类,这些类具有更广泛的暗魔法功能。由于OP显然使用的是Python 2.x,我认为您应该用这些术语给出答案,不只是说如果最后没有修改的话,这个版本就不能用了。@martineau事实上,我并没有检查OP使用的是什么版本,编辑。也是+1,因为它符合OP的要求,并提到了一些注意事项……尽管它忽略了这样一个事实,即它在某种程度上减慢了对
对象属性/方法的访问速度。我认为这是“要走的路”(如果我们想按照OP的要求去做),但是对我来说,
\uuu getattribute\uuuu
比元类更容易理解。
    super(clsobj, self).write(*args, **kwargs)