Overriding a class method in python

Context

I'm trying to use some of my "plugins" (I'm not sure if that's the correct definition for this). By "plugin" I mean a module that defines a model (this is scientific code) in such a way that it exists enough to be used elsewhere in the code.

Of course these plugins must follow a pattern that uses some modules / functions / classes defined in my code. Here is a small snippet for the relevant part of my code:

# [In the code]
class AllModels():
    def __init__(self):
        """
        Init.
        """
        self.count = 0

    def register(self, name, model):
        """
        Adds a model to the code
        """
        setattr(self, name, model)
        self.count += 1
        return

class Model():
    def __init__(self, **kwargs):
        """
        Some constants that defines a model
        """
        self.a = kwargs.get("a", None)
        self.b = kwargs.get("b", None)
        # and so on...

    def function1(self, *args, **kwargs):
        """
        A function that all models will have, but which needs:
            - to have a default behavior (when the instance is created)
            - to be redefinable by the "plugin" (ie. the model)
        """
        # default code for the default behavior
        return

instance = AllModels()

      

and here is the relevant "plugin" part:

# [in the plugin file]
from code import Model, instance
newmodel = Model(a="a name", b="some other stuff")

def function1(*args, **kwargs):
    """
    Work to do by this model
    """
    # some specific model-dependent work
    return

instance.register(newmodel)

      

Additional information and requirements

  • function1

    has exactly the same signature for any model plugin, but usually does a different job for each.

  • I would prefer the default behavior for function1

    so that if it's not defined by the plugin, I can still do something (try different possibilities and / or raise a warning / error).

  • The plugin function1

    may use some other functions that are only defined in this plugin. I am arguing this because the code works with the multiprocessing module and I need an instance instance

    AllModels

    to be able to be called function1

    in child processes. instance

    is defined in the parent process as well as in model plugins, but will be used in different child processes (however no changes have been made to it).

  • it would be great if function1

    , when "overridden" by a plugin, would be able to access the attributes of the instance Model

    (ie self

    ).

Problem

I've read many different sources of python documentation and some SO questions. I only see two / three possible solutions to this problem:

1) without declaring a method function1

in the class Model

, but simply setting it as an attribute when the plugin creates a new instance.

# [in the plugin file]
def function1(*args, **kwargs):
    # ....
    return
newmodel.function1 = function1

      

and then call it when needed. In this case, the attribute function1

on the object Model

will probably start with None

. One of the caveats is that function1

there is no β€œdefault behavior” for (it needs to be handled in code, for example, when testing if instance.function1 is None: ...

), and even more - I cannot access self

this way ...

2) using python decorators somehow. I've never used this, and the documentation I've read is not that straightforward (I mean not straightforward due to the sheer number of uses it has). But this seems to be a good solution. However, I am worried about its performance impact (I read that it can slow down the execution of the decorated function / method). If this solution is the best option, then I would like to know how to use it (maybe a quick snippet) and if class attributes can be used Model

:

# [in the plugin file]
@mydecorator
def function1(self, *args, **kwargs):
    """
    I'm not sure I can use *self*, but it would be great since some attributes of self are used for some other function similar to *function1*...
    """
    # some stuff using *self*, eg.:
    x = self.var **2 + 3.4
    # where self.var has been defined before, eg.: newmodel.var = 100.

      

3) using a module types

and its MethodType

... I'm not sure if this is relevant in my case ... but I could be wrong.

As you will probably see after this long question, I am not very good at such python functions and now my understanding of decorators is really awkward. While I was reading some documentation, I thought it might be worth asking a question here, since I'm not sure what direction to take to address my problem.

Decision

The beauty of Sender's answer is that it is really simple and obvious ... and skipping that is a shame. Sorry for contaminating SO with this question.

+3


source to share


3 answers


Well, if I'm wrong, you want to subclass Model

. This is similar to creating an instance Model

and replacing its attribute function1

with a function defined in the plugin module (i.e. your option 1); but it is much cleaner and takes care of all the details for you:

# [in the plugin file]
from code import Model, instance

class MyModel(Model):
    def function1(*args, **kwargs):
        """
        Work to do by this model
        """
        # some specific model-dependent work
        return

newmodel = MyModel(a="a name", b="some other stuff")
instance.register(newmodel)

      



Thus, all other methods (functions "attached" to the instance Model

) inherit from Model

; they will behave exactly the same, but function1

will be overridden and follow your customized definition function1

.

+5


source


What you are trying to do can be done in fairly simple ways - just by using methods with an object-oriented interface and taking advantage of what Python functions are also normal objects -

One simple thing is to simply have your own "model" class to take "function1" as a parameter and store it as a member of the object.

Some code like this, with minimal changes to your code - although much more interesting things are certainly possible:



# [In the code]

class AllModels():
    def __init__(self):
        """
        Init.
        """
        self.count = 0

    def register(self, name, **kwargs):
        """
        Adds a model to the code
        """
        model = Model(**kwargs)
        setattr(self, name, model)
        self.count += 1
        return

class Model():
    def __init__(self, **kwargs):
        """
        Some constants that defines a model
        """
        self.a = kwargs.get("a", None)
        self.b = kwargs.get("b", None)
        if "function1" in kwargs:
            self.real_function1 = kwargs["function1"]
            self.function1.__doc__ = kwargs["function1"].__doc__
        # and so on...

    def function1(self, *args, **kwargs):
        """
        A function that all models will have, but which needs:
            - to have a default behavior (when the instance is created)
            - to be redefinable by the "plugin" (ie. the model)
        """
        if self.real_function1:
            return self.real_function1(self, *args, **kwargs)
        # default code for the default behavior
        return

instance = AllModels()

      

and

# [in the plugin file]
from code import Model, instance
newmodel = Model(a="a name", b="some other stuff")

def function1(self, *args, **kwargs):
    """
    Work to do by this model
    """
    # some specific model-dependent work
    return


instance.register("name", function1 = function1, a="a name", b="some other stuff")

      

+2


source


Could you write a dummy function1()

function in the class Model

and raise

a NotImplementedError

? Thus, if anyone tries to inherit from Model

without implementation function1()

, they will get an exception when they try to run the code. If you use code for them, you can catch this error and return an error message to the user.

For example:

class Model:
    #Your code

    def function1():
        raise NotImplementedError("You need to implement function1 
            when you inherit from Model")

      

Then, when you run the code, you can do the following:

try:
    modelObj.function1()
except NotImplementedError as e:
    #Perform error handling here

      

EDIT: The official Python documentation for NotImplementedError says: "In custom base classes, abstract methods must throw this exception when they require a method to be overridden. This seems to fit the requirements here.

+1


source







All Articles