3

假设我有这个代码:

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 类。我尝试过使用 the__bases__和其他东西,但它不适用于我的目的。

4

4 回答 4

4

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

但我必须说,可以使用一些__getattribute__魔法:

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

但是请注意,这只是一个 hack,并且在使用一些继承时可能会中断,或者即使您write从类访问:

>>> Bar.write(b, 'Hello, World!')  #should be equivalent to b.write('Hello, World!')
Hello, World!
于 2013-02-14T17:11:07.243 回答
3

没有修改就没有(好的)方法可以做到这一点Bar()- 你想做的是使用super()inside Bar(),这将允许你调用父方法。

如果你正在使用一个你不能修改的类,那么最好的解决方案是制作一个包装类来手动执行你想要的操作,使用表现不佳的类。例如:

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()通过删除参数来使用更简单的语法。)

于 2013-02-14T17:03:05.633 回答
3

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

class ReverserMetaclass(type):
    def __new__(cls, name, bases, dct):
        """ This metaclass replaces methods of classes made from it
            with a version that first calls their base classes
        """
        # create a new namespace for the new class
        new_dct = {}
        for member_name, member in dct.items():
            # only decorate methods/callable in the new class
            if callable(member):
                member = cls.wrap(bases, member_name, member)
            new_dct[member_name] = member
        # construct the class
        return super(ReverserMetaclass, cls).__new__(cls, name, bases, new_dct)

        # instead of the above, you can also use something much simpler
        #     dct['read'] = cls.wrap(bases, 'read', dct['read'])
        #     return super(ReverserMetaclass, cls).__new__(cls, name, bases, dct)
        # if you have a specific method that you want to wrap and want to 
        # leave the rest alone

    @classmethod
    def wrap(cls, bases, name, method):
        """ this method calls methods in the bases before calling the method """
        def _method(*args, **kwargs):
            for base in bases:
                if hasattr(base, name):
                    getattr(base, name)(*args, **kwargs)
            # put this above the loop if you want to reverse the call order
            ret = method(*args, **kwargs)
            return ret
        return _method

示例控制台运行:

>>> 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 元类非常强大,尽管正如其他人所说,你真的不想太频繁地使用这种魔法。

于 2013-02-14T17:24:19.130 回答
1

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

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: test
-From Foo
Baz: test
-From Foo

如果您希望子类write()方法之后调用基类的版本而不是根 ( Foo) 类的版本,只需更改硬编码:

    Foo.write(self, *args, **kwargs)

拨电至:

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

Foo.__new__().

于 2013-02-14T18:53:02.743 回答