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!")

最後の呼び出しは、明らかにhelloworldを単独で出力します。「-FromFoo」も作成する必要がありますが、Barクラスを変更せずに、Fooクラスのみを作成します。と他のものを使ってみました__bases__が、私の目的にはうまくいきません。

4

4 に答える 4

4

私はLattywareに100%同意します:これを行うべきではありません。親クラスは、サブクラスまたはそれらがどのように機能するかについて「知っている」べきではありません。

__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

ただし、これは単なるハックであり、少しの継承を使用する場合、または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()内部で使用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

これは、メタクラスマジックを使用してそれを行う1つの方法です。IMHO、他のアプローチよりも堅牢で柔軟性があり、無制限の呼び出し(例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 に答える