简体   繁体   中英

Overriding an inner function of a method in python

That is a kind of best practices question.

I have a class structure with some methods defined. In some cases I want to override a particular part of a method. First thought on that is splitting my method to more atomic pieces and override related parts like below.

class myTest(object):
    def __init__(self):
        pass

    def myfunc(self):
        self._do_atomic_job()
        ...
        ...

    def _do_atomic_job(self):
        print "Hello"

That is a practical-looking way to solve the problem. But since I have too many parameters that is needed to be transferred to and revieced back from _do_atomic_job() , I do not want to pass and retrieve tons of parameters. Other option is setting these parameters as class variables with self.param_var etc but those parameters are used in a small part of the code and using self is not my preferred way of solving this.

Last option I thought is using inner functions. (I know I will have problems in variable scopes but as I said, this is a best practise and just ignore them and think scope and all things about the inner functions are working as expected)

class MyTest2(object):
    mytext = ""

    def myfunc(self):
        def _do_atomic_job():
            mytext = "Hello"
        _do_atomic_job()
        print mytext

Lets assume that works as expected. What I want to do is overriding the inner function _do_atomic_job()

class MyTest3(MyTest2):
    def __init__(self):
        super(MyTest3, self).__init__()
        self.myfunc._do_atomic_job = self._alt_do_atomic_job  # Of course this do not work!

    def _alt_do_atomic_job(self):
        mytext = "Hollla!"

Do what I want to achieve is overriding inherited class' method's inner function _do_atomic_job

Is it possible?

Either factoring _do_atomic_job() into a proper method, or maybe factoring it into its own class seem like the best approach to take. Overriding an inner function can't work, because you won't have access to the local variable of the containing method.

You say that _do_atomic_job() takes a lot of parameters returns lots of values. Maybe you group some of these parameters into reasonable objects:

_do_atomic_job(start_x, start_y, end_x, end_y) # Separate coordinates
_do_atomic_job(start, end)                     # Better: start/end points
_do_atomic_job(rect)                           # Even better: rectangle

If you can't do that, and _do_atomic_job() is reasonably self-contained, you could create helper classes AtomicJobParams and AtomicJobResult . An example using namedtuples instead of classes:

AtomicJobParams = namedtuple('AtomicJobParams', ['a', 'b', 'c', 'd'])

jobparams = AtomicJobParams(a, b, c, d)
_do_atomic_job(jobparams)                # Returns AtomicJobResult

Finally, if the atomic job is self-contained, you can even factor it into its own class AtomicJob .

class AtomicJob:
    def __init__(self, a, b, c, d):
        self.a = a
        self.b = b
        self.c = c
        self.d = d
        self._do_atomic_job()

    def _do_atomic_job(self):
        ...
        self.result_1 = 42
        self.result_2 = 23
        self.result_3 = 443

Overall, this seems more like a code factorization problem. Aim for rather lean classes that delegate work to helpers where appropriate. Follow the single responsibility principle . If values belong together, bundle them up in a value class.

As David Miller (a prominent Linux kernel developer) recently said :

If you write interfaces with more than 4 or 5 function arguments, it's possible that you and I cannot be friends.

Inner variables are related to where they are defined and not where they are executed. This prints "hello".

class MyTest2(object):
    def __init__(self):
        localvariable = "hello"
        def do_atomic_job():
            print localvariable
        self.do_atomic_job = do_atomic_job

    def myfunc(self):
        localvariable = "hollla!"
        self.do_atomic_job()


MyTest2().myfunc()

So I can't see any way you could use the local variables without passing them, which is probably the best way to do it.

Note: Passing locals() will get you a dict of the variables, this is considered quite bad style though.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM