0

Im having trouble understanding clean way to do this. I would like a function named set_delay() that take a variety of parameters. I have 3 different "delay types" that can be set: constant, uniform, and normal. Here is what I currently have:

def set_delay_constant(delay):
    continue

def set_delay_uniform(min_delay, max_delay):
    continue

def set_delay_normal(mean, std_dev, timeout):
    continue

The problem I have with the above is that about ~80% of the code in each function is repeated. Ideas Ive seen are:

def set_delay(delay_type, delay=None, min_delay=None, max_delay=None, mean=None, std_dev=None, timeout=None):
    continue

But when I need to extend this with more delay types, I can see this getting very long and hard to read. What is the most "pythonic" way to go about this? Thank you!

7
  • That code's not valid... SyntaxError: 'continue' not properly in loop. Did you mean pass instead of continue? Commented Dec 22, 2021 at 20:00
  • 1
    You may use keyword parameters. Commented Dec 22, 2021 at 20:02
  • @wjandrea Yes just as a way to only include the function header Commented Dec 22, 2021 at 20:05
  • 3
    I would consider three separate function the right way to do this. You can factor out the common parts to a single function that each of your three calls. Commented Dec 22, 2021 at 20:11
  • 1
    @chepner I just posted an answer to that effect. Thanks for reassuring me that I'm on the right track :) If you have anything to add from your experience, I'd appreciate it. Commented Dec 22, 2021 at 20:21

2 Answers 2

6

Don't use overloading. Where the problem that you're trying to solve is "about ~80% of the code in each function is repeated", the better solution is to factor out that code into its own function, call it _set_delay for example, then call it from each of the set_delay_* functions.

To me, overloading only makes sense where the parameters overlap, but none do in your case. For contrast, say I write range(10) but then I realize I want to start from 1; thanks to overloading, I can simply change it to range(1, 10). Meanwhile, if I added an argument to your proposed set_delay, I would get totally different behaviour.

Sign up to request clarification or add additional context in comments.

6 Comments

Well, strictly speaking, Python doesn't have overloading. But your answer hits the key issue
@juanpa I thought I might have the terminology a bit off, but what do you mean exactly? What do you call the range example if not overloading? I read about multiple dispatch, but that seems to be only about parameter type, not number of parameters.
@wjandrea like in Java, where you define multiple functions with the same name but different signatures, and the actual function that is called depends on the arguments provided to the function (usually decidd at compile time). So checkout this C++ example on wikipedia. Python doesn't support that, I'm not sure any dynamic language supports it. But usually, just providing a very flexible function signature and manually implementing the dispatch yourself is frequently what you'd do in Python.
I consider range something that retains its API despite the shortcomings, due to its long history. If it were introduced today, I suspect it would have two required positional arguments, with an optional third to specify the step size. It might also make more extensive use of keyword-only arguments.
Compare range to enumerate: range(10) could still be the numbers 0 to 9, with range(10, start=5) being the numbers 5 to 9 (though being able to have 5 and 10 "out of order" looks odd). I don't know what the best solution would be.
|
1

I would personally move to a class based structure:

from abc import ABC, abstractmethod

class Delay(ABC):
    def do_delay(self):
        self._prepare_delay()
        #put the 80% of overlapping code here

    @abstractmethod
    def _prepare_delay(self):
        pass


class ConstantDelay(Delay):
    def __init__(self, delay):
        self.delay = delay
        #custom code can go here

    def _prepare_delay(self)
        #more custom code here

class UniformDelay(Delay):
    def __init__(self, min_delay, max_delay):
        self.min_delay = min_delay
        self.max_delay = max_delay
        #custom code goes here

    def _prepare_delay(self)
        #more custom code here

#etc

Then your set_delay function would take a single argument, an instance of a Delay subclass:

def set_delay(delay):
    pass

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.