如何在Python中的DerivedClass方法覆盖之前执行BaseClass方法

时间:2014-05-21 08:42:16

标签: python oop inheritance

我几乎可以确定我想要做的事情有一个合适的术语,但由于我不熟悉它,我将尝试明确地描述整个想法。所以我拥有的是一组所有都继承自一个基类的类。所有类几乎完全由不同的方法组成,这些方法仅在每个类中相关。但是,有几种方法具有相似的名称,一般功能和一些逻辑,但它们的实现仍然大多不同。所以我想知道的是,是否可以在基类中创建一个方法,该方法将执行一些类似于所有方法的逻辑,但仍然继续在特定于类的方法中执行。希望这是有道理的,但我会尝试给出我想要的基本例子。

因此,请考虑一个类似的基类

class App(object):

    def __init__(self, testName):
        self.localLog = logging.getLogger(testName)

    def access(self):
        LOGIC_SHARED

派生类

的示例
class App1(App):

    def __init__(self, testName):
        . . .   
        super(App1, self).__init__(testName)

    def access(self):
        LOGIC_SPECIFIC

所以我想要实现的是在调用任何LOGIC_SHARED方法时要执行的基类access方法中的access部分{}之前执行App部分之前的LOGIC_SPECIFIC,这是(如其所说)特定于所有派生类的每个access方法的部分。

如果这有任何区别,LOGIC_SHARED主要包括日志记录和维护任务。

希望这很清楚,这个想法是有道理的。

注1 LOGIC_SHARED部分中使用了特定于类的参数。

注意2 : 仅使用Python内置函数和模块来实现该行为非常重要。

注3 LOGIC_SHARED部分看起来像这样:

try:
    self.localLog.info("Checking the actual link for %s", self.application)
    self.link = self.checkLink(self.application)
    self.localLog.info("Actual link found!: %s", self.link)
except:
    self.localLog.info("No links found. Going to use the default link: %s", self.link)

因此,我使用了很多特定的类实例属性,我不确定如何使用基类中的这些属性。

6 个答案:

答案 0 :(得分:11)

当然,只需将特定逻辑放在自己的“私有”函数中,该函数可以被派生类覆盖,并将access保留在Base中。

class Base(object):
    def access(self):
        # Shared logic 1
        self._specific_logic()
        # Shared logic 2

    def _specific_logic(self):
        # Nothing special to do in the base class
        pass

        # Or you could even raise an exception
        raise Exception('Called access on Base class instance')


class DerivedA(Base):
    # overrides Base implementation
    def _specific_logic(self):
        # DerivedA specific logic

class DerivedB(Base):
    # overrides Base implementation
    def _specific_logic(self):
        # DerivedB specific logic

def test():
    x = Base()
    x.access()           # Shared logic 1
                         # Shared logic 2

    a = DerivedA()
    a.access()           # Shared logic 1
                         # Derived A specific logic
                         # Shared logic 2

    b = DerivedB()
    b.access()           # Shared logic 1
                         # Derived B specific logic
                         # Shared logic 2

答案 1 :(得分:4)

执行所需操作的最简单方法是在子级access方法中调用父级的access方法。

class App(object):
    def __init__(self, testName):
        self.localLog = logging.getLogger(testName)

    def access(self):
        LOGIC_SHARED

class App1(App):
    def __init__(self, testName):
        super(App1, self).__init__(testName)

    def access(self):
        App.access(self)
        # or use super
        super(App1, self).access()

但是,您的共享功能主要是日志记录和维护。除非有一个迫切的理由将它放在父类中,否则您可能要考虑将共享功能重构为装饰器函数。如果您想为类中的一系列方法重用类似的日志记录和维护功能,这将非常有用。

您可以在此处阅读有关函数修饰器的更多信息:http://www.artima.com/weblogs/viewpost.jsp?thread=240808,或者在Stack Overflow上阅读:How to make a chain of function decorators?

def decorated(method):
    def decorated_method(self, *args, **kwargs):
        LOGIC_SHARED
        method(self, *args, **kwargs)
    return decorated_method

请记住,在python中,函数是第一类对象。这意味着您可以获取一个函数并将其作为参数传递给另一个函数。装饰器功能利用这一点。装饰器函数将另一个函数作为参数(此处称为方法),然后创建一个新函数(此处称为decorated_method),取代原始函数。

您的App1课程将如下所示:

class App1(App):
    @logged
    def access(self):
        LOGIC_SPECIFIC

这确实是这方面的简写:

class App1(App):
    def access(self):
        LOGIC_SPECIFIC

decorated_access = logged(App.access)
App.access = decorated_access

我会发现这比向超类添加方法以捕获共享功能更优雅。

答案 2 :(得分:1)

如果我理解这个通知(How to execute BaseClass method before it gets overridden by DerivedClass method in Python)你希望传递给派生类中使用的父类的其他参数

基于Jonathon Reinhart's answer

你可以这样做

class Base(object):
    def access(self,
                    param1 ,param2, #first common parameters
                    *args,          #second positional parameters
                    **kwargs        #third keyword arguments
               ):
        # Shared logic 1
        self._specific_logic(param1, param2, *args, **kwargs)
        # Shared logic 2

    def _specific_logic(self, param1, param2, *args, **kwargs):
        # Nothing special to do in the base class
        pass

        # Or you could even raise an exception
        raise Exception('Called access on Base class instance')


class DerivedA(Base):
    # overrides Base implementation
    def _specific_logic(self, param1, param2, param3):
        # DerivedA specific logic

class DerivedB(Base):
    # overrides Base implementation
    def _specific_logic(self, param1, param2, param4):
        # DerivedB specific logic

def test():
    x = Base()

    a = DerivedA()
    a.access("param1", "param2", "param3")           # Shared logic 1
                                                     # Derived A specific logic
                                                     # Shared logic 2

    b = DerivedB()
    b.access("param1", "param2", param4="param4")   # Shared logic 1
                                                    # Derived B specific logic
                                                    # Shared logic 2

答案 3 :(得分:1)

我个人更喜欢Jonathon Reinhart的回答,但看到你似乎想要更多的选择,这里还有两个。我可能从不使用元类,虽然很酷,但我可能会考虑使用装饰器的第二个。

使用元类

此方法使用基类的元类,它将强制首先调用基类的访问方法,而不需要单独的私有函数,也不必显式调用super或类似的东西那。最终结果:没有额外的工作/代码进入继承类。

另外,它的作用类似于 maaaagiiiiic </spongebob>

以下是执行此操作的代码。在这里http://dbgr.cc/W,您可以直接浏览代码,看看它是如何工作的:

#!/usr/bin/env python

class ForceBaseClassFirst(type):
    def __new__(cls, name, bases, attrs):
        """
        """
        print("Creating class '%s'" % name)

        def wrap_function(fn_name, base_fn, other_fn):
            def new_fn(*args, **kwargs):
                print("calling base '%s' function" % fn_name)
                base_fn(*args, **kwargs)
                print("calling other '%s' function" % fn_name)
                other_fn(*args, **kwargs)

            new_fn.__name__ = "wrapped_%s" % fn_name
            return new_fn

        if name != "BaseClass":
            print("setting attrs['access'] to wrapped function")
            attrs["access"] = wrap_function(
                "access",
                getattr(bases[0], "access", lambda: None),
                attrs.setdefault("access", lambda: None)
            )

        return type.__new__(cls, name, bases, attrs)

class BaseClass(object):
    __metaclass__ = ForceBaseClassFirst

    def access(self):
        print("in BaseClass access function")


class OtherClass(BaseClass):
    def access(self):
        print("in OtherClass access function")

print("OtherClass attributes:")
for k,v in OtherClass.__dict__.iteritems():
    print("%15s: %r" % (k, v))

o = OtherClass()

print("Calling access on OtherClass instance") 
print("-------------------------------------")
o.access()

这使用元类替换OtherClass的访问功能,其功能包含对BaseClass访问功能的调用以及对OtherClass的调用的访问功能。请在此处查看元类的最佳解释https://stackoverflow.com/a/6581949

单步执行代码应该可以帮助您理解事物的顺序。

使用装饰器

此功能也可以轻松放入装饰器中,如下所示。同样,可以在此处找到以下代码的可执行/可调试/可运行版本http://dbgr.cc/0

#!/usr/bin/env python

def superfy(some_func):
    def wrapped(self, *args, **kwargs):
        # NOTE might need to be changed when dealing with
        # multiple inheritance
        base_fn = getattr(self.__class__.__bases__[0], some_func.__name__, lambda *args, **kwargs: None)

        # bind the parent class' function and call it
        base_fn.__get__(self, self.__class__)(*args, **kwargs)

        # call the child class' function
        some_func(self, *args, **kwargs)

    wrapped.__name__ = "superfy(%s)" % some_func.__name__
    return wrapped

class BaseClass(object):
    def access(self):
        print("in BaseClass access function")


class OtherClass(BaseClass):
    @superfy
    def access(self):
        print("in OtherClass access function")

print("OtherClass attributes")
print("----------------------")
for k,v in OtherClass.__dict__.iteritems():
    print("%15s: %r" % (k, v))
print("")

o = OtherClass()

print("Calling access on OtherClass instance") 
print("-------------------------------------")
o.access()

上面的装饰者检索BaseClass&#39;相同名称的功能,并在调用OtherClass之前先调用&#39;功能

答案 4 :(得分:1)

这种简单的方法可以提供帮助。

class App:

    def __init__(self, testName):

        self.localLog = logging.getLogger(testName)

        self.application = None
        self.link = None

    def access(self):
        print('There is something BaseClass must do')
        print('The application is ', self.application)
        print('The link is ', self.link)


class App1(App):

    def __init__(self, testName):

        # ...
        super(App1, self).__init__(testName)

    def access(self):
        self.application = 'Application created by App1'
        self.link = 'Link created by App1'

        super(App1, self).access()

        print('There is something App1 must do')


class App2(App):

    def __init__(self, testName):

        # ...
        super(App2, self).__init__(testName)

    def access(self):
        self.application = 'Application created by App2'
        self.link = 'Link created by App2'

        super(App2, self).access()

        print('There is something App2 must do')

和测试结果:

>>> 
>>> app = App('Baseclass')
>>> app.access()
There is something BaseClass must do
The application is  None
The link is  None
>>> app1 = App1('App1 test')
>>> app1.access()
There is something BaseClass must do
The application is  Application created by App1
The link is  Link created by App1
There is something App1 must do
>>> app2 = App2('App2 text')
>>> app2.access()
There is something BaseClass must do
The application is  Application created by App2
The link is  Link created by App2
There is something App2 must do
>>> 

答案 5 :(得分:0)

添加一个组合功能,我们可以组合两个功能并一个接一个地执行它们,如下所示

def combine(*fun):
    def new(*s):
        for i in fun:
            i(*s)
    return new


class base():
    def x(self,i):
        print 'i',i

class derived(base):
    def x(self,i):
        print 'i*i',i*i
    x=combine(base.x,x)

new_obj=derived():
new_obj.x(3)

输出Bellow

i 3
i*i 9

它不必是单级层次结构,它可以具有任意数量的级别或嵌套