1

I am almost sure that there is a proper term for what I want to do but since I'm not familiar with it, I will try to describe the whole idea explicitly. So what I have is a collection of classes that all inherit from one base class. All the classes consist almost entirely of different methods that are relevant within each class only. However, there are several methods that share similar name, general functionality and also some logic but their implementation is still mostly different. So what I want to know is whether it's possible to create a method in a base class that will execute some logic that is similar to all the methods but still continue the execution in the class specific method. Hopefully that makes sense but I will try to give a basic example of what I want.

So consider a base class that looks something like that:

class App(object):

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

    def access(self):
        LOGIC_SHARED

And an example of a derived class:

class App1(App):

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

    def access(self):
        LOGIC_SPECIFIC

So what I'd like to achieve is that the LOGIC_SHARED part in base class access method to be executed when calling the access method of any App class before executing the LOGIC_SPECIFIC part which is(as it says) specific for each access method of all derived classes.

If that makes any difference, the LOGIC_SHARED mostly consists of logging and maintenance tasks.

Hope that is clear enough and the idea makes sense.

NOTE 1: There are class specific parameters which are being used in the LOGIC_SHARED section.

NOTE 2: It is important to implement that behavior using only Python built-in functions and modules.

NOTE 3: The LOGIC_SHARED part looks something like that:

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)

So, there are plenty of specific class instance attributes that I use and I'm not sure how to use these attributes from the base class.

Eugene S
  • 6,709
  • 8
  • 57
  • 91
  • 1
    If you show an example of these "class specific parameters" I can show you how to use the same technique I described to abstract them out of the problem as well. – Jonathon Reinhart Jun 12 '14 at 13:30
  • @JonathonReinhart Thank very much for your effort. It is very appreciated! I have added **NOTE 3** with an example of a shared logic part for your reference. Please let me know if I can provide more info. – Eugene S Jun 13 '14 at 06:36
  • So are you saying that `self.application` is not valid in a different implementation of the base class? Basically, you need to just hide class-specific details behind methods that are overridden in different ways in the child classes. – Jonathon Reinhart Jun 17 '14 at 14:07
  • Isn't `super(App1, self).access()` enough? just like with the constructor. – solarc Jun 17 '14 at 18:02
  • @JonathonReinhart `self.application` is not defined in Base Class. It is defined in the derived class. Since each derived class represents an application, `self.application` has the specific application name. The same about the `self.link`. I hope I understood your question though. – Eugene S Jun 18 '14 at 03:20
  • So what's wrong with just referencing `self.application` in the shared logic portion? – Jonathon Reinhart Jun 18 '14 at 05:24
  • @JonathonReinhart hmm.. because if I move this part (whatever is mentioned in NOTE3) into the Base Class, I won't have access to the `self` attributes. Again, this is as far as I understand that. Thanks – Eugene S Jun 18 '14 at 05:34
  • Why not? I think you may be missing the primary beauty of OOP. Even though you may be executing in a base-class method, `self` still points to the original derived type. Just try it with a simple example, I think you'll see what I mean. – Jonathon Reinhart Jun 18 '14 at 05:36
  • See [this example](http://ideone.com/Hk13CM). – Jonathon Reinhart Jun 18 '14 at 05:42
  • @JonathonReinhart Yes I definitely wasn't aware of that part(using attributes of derived class in base class). Your example makes it much more clear. I will try to implement your solution and hopefully it will work as expected. Thank you so much for your effort. – Eugene S Jun 18 '14 at 06:05

6 Answers6

11

Sure, just put the specific logic in its own "private" function, which can overridden by the derived classes, and leave access in the 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
Jonathon Reinhart
  • 132,704
  • 33
  • 254
  • 328
  • Hi and thanks for your answer. But does that mean that I will have to call `_specific_logic` function instead 'access' for each derived class? – Eugene S May 21 '14 at 08:58
  • @EugeneS Nope, that wouldn't make any sense, because `access` wouldn't be involved at all then. The derived classes inherit everything that `Base` has, including `access`. – Jonathon Reinhart May 21 '14 at 08:59
  • Thanks again. I get it now. However there are some class specific parameters that 'access' method is using. Not sure how can I access these parameters from my base class? – Eugene S May 21 '14 at 09:28
  • @EugeneS The same way I've shown with the specific functions. Either assign default values to them in the base class, or implement getter functions which work just like I've shown. – Jonathon Reinhart May 21 '14 at 09:31
4

The easiest method to do what you want is to simply call the parent's class access method inside the child's access method.

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()

However, your shared functionality is mostly logging and maintenance. Unless there is a pressing reason to put this inside the parent class, you may want to consider is to refactor the shared functionality into a decorator function. This is particularly useful if you want to reuse similar logging and maintenance functionality for a range of methods inside your class.

You can read more about function decorators here: http://www.artima.com/weblogs/viewpost.jsp?thread=240808, or here on 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

Remember than in python, functions are first class objects. That means that you can take a function and pass it as a parameter to another function. A decorator function make use of this. The decorator function takes another function as a parameter (here called method) and then creates a new function (here called decorated_method) that takes the place of the original function.

Your App1 class then would look like this:

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

This really is shorthand for this:

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

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

I would find this more elegant than adding methods to the superclass to capture shared functionality.

Community
  • 1
  • 1
Hans Then
  • 10,935
  • 3
  • 32
  • 51
  • Hi. Thanks for your answer and the idea. It sounds very interesting! However could you please explain a bit more about the syntax of the decorator function and how to apply it to a function. I'd also like to know if that's possible to store that decorator function in a separate file? Thanks again – Eugene S Jun 12 '14 at 08:17
  • I have updated the answer a bit to explain decorators (and point to a few other articles). A decorator function can be any normal function that takes a callable (a function or a method) as its only argument, so you can define it in a separate file. – Hans Then Jun 12 '14 at 08:37
  • +1 Using inheritance to do this sort of common code extraction is almost always a bad idea, since it is inflexible and leads to complex and fragile class hierarchies. – Jürgen Strobel Jun 16 '14 at 08:51
  • 1
    Like I tell my son, parents are not invented to do household chores for their children. – Hans Then Jun 17 '14 at 09:01
1

If I understand well this commment (How to execute BaseClass method before it gets overridden by DerivedClass method in Python) you want that additional arguments passed to the parent class used in derived class

based on Jonathon Reinhart's answer

it's how you could do

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
Community
  • 1
  • 1
Xavier Combelle
  • 10,968
  • 5
  • 28
  • 52
1

I personally prefer Jonathon Reinhart's answer, but seeing as you seem to want more options, here's two more. I would probably never use the metaclass one, as cool as it is, but I might consider the second one with decorators.

With Metaclasses

This method uses a metaclass for the base class that will force the base class's access method to be called first, without having a separate private function, and without having to explicitly call super or anything like that. End result: no extra work/code goes into inheriting classes.

Plus, it works like maaaagiiiiic </spongebob>

Below is the code that will do this. Here http://dbgr.cc/W you can step through the code live and see how it works :

#!/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()

This uses a metaclass to replace OtherClass's access function with a function that wraps a call to BaseClass's access function and a call to OtherClass's access function. See the best explanation of metaclasses here https://stackoverflow.com/a/6581949.

Stepping through the code should really help you understand the order of things.

With Decorators

This functionality could also easily be put into a decorator, as shown below. Again, a steppable/debuggable/runnable version of the code below can be found here 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()

The decorator above retrieves the BaseClass' function of the same name, and calls that first before calling the OtherClass' function.

Community
  • 1
  • 1
nOOb cODEr
  • 236
  • 1
  • 4
1

May this simple approach can help.

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')

and the test result:

>>> 
>>> 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
>>> 
Tok Soegiharto
  • 329
  • 1
  • 8
0

Adding a combine function we can combine two functions and execute them one after other as bellow

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)

Output Bellow

i 3
i*i 9

it need not be single level hierarchy it can have any number of levels or nested

Mr. A
  • 1,221
  • 18
  • 28