2

I have a rather odd scenario, and I just can't wrap my head around the proper solution. I'm working on a project that has multiple functions to perform certain operations:

  • These functions are interconnected and need to be able to call each other (and inherit the parameters of their parent object).
  • Each operation should be extensible and be able to have new functions added.
  • There should be a default function for each operation.

Let me provide a simple example. Consider a class mymath that can perform add and multiply. multiply can be performed by simple multiplication or by iteratively calling the add function (let's pretend that the + operator doesn't exist in order to demonstrate the requirement that the functions need to be able to call one another). Here's an example using nested classes:

class addition_N:
    def add(self, obj):
        result = obj.param1 + obj.param2
        return result

class multiply_N:
    def multiply(self, obj):
        return obj.param1 * obj.param2

    def mult_loop(self, obj):
        param1 = obj.param1
        obj.param1 = 0

        for i in range(param1):
            obj.param1 = getattr(mymath.addition, 'add')(self, obj=obj)

        return obj.param1

class mymath:
    def __init__(self, param1, param2):
        self.param1 = param1
        self.param2 = param2

    class multiply(multiply_N):
        def __new__(cls, obj, func="multiply"):
            return getattr(cls, func)(cls, obj=obj)

    class addition(addition_N):
        def __new__(cls, obj, func="add"):
            return getattr(cls, func)(cls, obj=obj)


math = mymath(param1=2, param2=3)
mult = math.multiply(math, func='multiply')

math = mymath(param1=2, param2=3)
mult_loop = math.multiply(math, func='mult_loop')

math = mymath(param1=2, param2=3)
add = math.addition(math, func='add')

print(mult)
print(mult_loop)
print(add)

(Please don't slay me for using __new__. My actual project code has some conditional statements in there to handle different scenarios. If you have suggestions for more Pythonic alternatives, let me know - I may be able to modify them to fit my needs).

The code is more easily extended, but I have a scoping issue. To solve the scoping issue, I pass the entire object, obj, through to each function. This works, but it is messy. (Before suggesting that I should just copy the parameters of the parent function and instantiate new objects, my actual project is quite complex, has a LOT of parameters, and that would kill performance).

What am I missing, here? I feel like there is a simple solution right under my nose, but I've been staring at my code for so long that I can't figure it out.

EDIT 2020-05-04 I removed the eval calls from my example. I didn't know about using getattr. I'm still not sure how to handle the scoping issue.

EDIT 2020-05-05 I broke things out into sub-classes. This more closely aligns with my end goal. Users will be provided with the _N classes to extend, and the code will be included in my main mymath class at runtime.

I still can't figure out a good way to get around the scoping issue with obj, though. Thoughts?

  • 1
    stop using eval, it is unecessary and adds a lot of drawbacks for no benefit – juanpa.arrivillaga May 04 '20 at 22:44
  • Definitely don't use the eval and nested function approach. That's just so dirty :D I have a strong feeling that the solution to your troubles is to take a step or two back and explain the larger context of what you are trying to achieve, and that could probably point to a better general architecture, so that you then don't run into the sub-issue of nested functions having to call each other. – Lagerbaer May 04 '20 at 22:44
  • 1
    to avoid eval, pass the function objects themselves, and then just call them. Or if you want to evaluate a method, then `getattr(self, 'multiply')(obj=obj)` – juanpa.arrivillaga May 04 '20 at 22:47
  • Oh, trust me - I don't want to use `eval`. I'd like to avoid it. It's SO wrong. I just can't figure out a better way. As for better explaining the larger context, this is a pretty accurate (yet simplified) example of what my project is trying to achieve. I have multiple operations (each with different possible functions for calculating them). The operations are all dependent upon one another and thus must be able to call each other while inheriting properties in the process. Yikes. – Arthur Sommers May 04 '20 at 22:48
  • 1
    It's unclear to me why you need your extra stuff to be embedded within your existing methods. Why not make `add` a method of the class? Why not have both `multiply` and `mult_loop` methods? If you want an additional method that switches between them, it would be so much easier to just have it call `self.mult()` or something than to mess around with nested things or `eval`. – Blckknght May 04 '20 at 23:02
  • @juanpa.arrivillaga: Isn’t that just `self.multiply(obj=obj)`? – Davis Herring May 05 '20 at 03:13

0 Answers0