Наследование в Python такое, что все базовые функции вызываются - PullRequest
1 голос
/ 07 декабря 2009

По сути, я хочу сделать это:

class B:
    def fn(self):
        print 'B'

class A:
    def fn(self):
        print 'A'

@extendInherit
class C(A,B):
    pass

c=C()
c.fn()

И иметь вывод

A
B

Как мне реализовать декоратор exteInherit?

Ответы [ 3 ]

4 голосов
/ 07 декабря 2009

Это не работа для декораторов. Вы хотите полностью изменить нормальное поведение класса, так что это фактически работа для метакласса.

import types

class CallAll(type):
    """ MetaClass that adds methods to call all superclass implementations """
    def __new__(meta, clsname, bases, attrs):
        ## collect a list of functions defined on superclasses
        funcs = {}
        for base in bases:
            for name, val in vars(base).iteritems():
                if type(val) is types.FunctionType:
                    if name in funcs:
                        funcs[name].append( val )
                    else:
                        funcs[name] = [val]

        ## now we have all methods, so decorate each of them
        for name in funcs:
            def caller(self, *args,**kwargs):
                """ calls all baseclass implementations """
                for func in funcs[name]:
                    func(self, *args,**kwargs)
            attrs[name] = caller

        return type.__new__(meta, clsname, bases, attrs)

class B:
    def fn(self):
        print 'B'

class A:
    def fn(self):
        print 'A'

class C(A,B, object):
    __metaclass__=CallAll

c=C()
c.fn()
1 голос
/ 07 декабря 2009

Метакласс является возможным решением, но несколько сложным. super может сделать это очень просто (конечно, с новыми классами стилей: нет причин использовать устаревшие классы в новом коде!):

class B(object):
    def fn(self):
        print 'B'
        try: super(B, self).fn()
        except AttributeError: pass

class A(object):
    def fn(self):
        print 'A'
        try: super(A, self).fn()
        except AttributeError: pass

class C(A, B): pass

c = C()
c.fn()

Вам нужно попробовать / исключить, чтобы поддерживать любой порядок одиночного или множественного наследования (поскольку в некоторый момент не будет больше базы вдоль порядка-разрешения-метода, MRO, определяющего метод с именем fn, вам необходимо поймать и игнорировать полученное AttributeError). Но, как вы видите, в отличие от того, что вы думаете на основании вашего комментария к другому ответу, вам не обязательно переопределять fn в вашем самом крайнем классе, если вам не нужно делать что-то определенное для этого класса в таком переопределении - super прекрасно работает и с чисто унаследованными (не переопределенными) методами!

1 голос
/ 07 декабря 2009

Лично я бы не пытался делать это с декоратором, так как при использовании классов нового стиля и super() можно достичь следующего:

>>> class A(object):
...     def __init__(self):
...         super(A, self).__init__()
...         print "A"
... 
>>> class B(object):
...     def __init__(self):
...         super(B, self).__init__()
...         print "B"
... 
>>> class C(A, B):
...     def __init__(self):
...         super(C, self).__init__()
... 
>>> foo = C()
B
A

Я бы предположил, что вызовы методов будут работать так же.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...