Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass down multiple parameter through several functions

Let's assume we have an exposed function (Level 0). We call this function with various parameter. Internally this function calls a second function (Level 1) but does not use any of the given parameters other than calling a third function (Level 2) with them as arguments. It might do some other stuff however.

My Question is. How can we pass down the arguments without creating too much noise in the middle layer function (Level 1)? I list some possible ways beneath. Be warned however that some of them are rather ugly and only there for completeness reasons. I'm looking for some established guideline rather than individual personal opinion on the topic

# Transport all of them individually down the road.
# This is the most obvious way. However the amount of parameter increases the
# noise in A_1 since they are only passed along
def A_0(msg, term_print):
    A_1(msg, term_print)

def A_1(msg, term_print):
    A_2(msg, term_print)

def A_2(msg, term_print):
    print(msg, end=term_print)


# Create parameter object (in this case dict) and pass it down.
# Reduces the amount of parameters. However when only reading the source of B1
# it is impossible to determine what par is
def B_0(msg, term_print):
    B_1({'msg': msg, 'end': term_print})

def B_1(par):
    B_2(par)

def B_2(par):
    print(par['msg'], end=par['end'])


# Use global variables. We all know the pitfalls of global variables. However
# in python there are at least limited to their module
def C_0(msg, term_print):
    global MSG, TERM_PRINT

    MSG = msg
    TERM_PRINT = term_print
    C_1()

def C_1():
    C_2()

def C_2():
    print(MSG, end=TERM_PRINT)


# Use the fact that python creates function objects. We can now append those
# objects. This makes some more 'localised' variables than shown before. However
# this also makes the code harder to maintain. When we change D_2 we have to alter
# D_0 as well even though it never directly calls it
def D_0(msg, term_print):
    D_2.msg = msg
    D_2.term_print = term_print
    D_1()

def D_1():
    D_2()

def D_2():
    print(D_2.msg, end=D_2.term_print)


# Create a class with the functions E_1, E_2 to enclose the variables.
class E(dict):
    def E_1(self):
        self.E_2()

    def E_2(self):
        print(self['msg'], end=self['end'])

def E_0(msg, term_print):
    E([('msg', msg), ('end', term_print)]).E_1()


# Create a nested scope. This make it very hard to read the function. Furthermore
# F_1 cannot be called directly from outside (without abusing the construct)
def F_0(msg, term_print):
    def F_1():
        F_2()

    def F_2():
        print(msg, end=term_print)

    F_1()


A_0('What', ' ')
B_0('is', ' ')
C_0('the', ' ')
D_0('best', ' ')
E_0('way', '')
F_0('?', '\n')
like image 492
magu_ Avatar asked Feb 17 '16 17:02

magu_


1 Answers

It's hard to give a complete answer without knowing the full specifics of why there are so many parameters and so many levels of functions. But in general, passing too many parameters is considered a code smell.

Generally, if a group of functions all make use of the same parameters, it means they are closely related in some way, and may benefit from encapsulating the parameters within a Class, so that all the associated methods can share that data.

TooManyParameters is often a CodeSmell. If you have to pass that much data together, it could indicate the data is related in some way and wants to be encapsulated in its own class. Passing in a single data structure that belongs apart doesn't solve the problem. Rather, the idea is that things that belong together, keep together; things that belong apart, keep apart; per the OneResponsibilityRule.

Indeed, you may find that entire functions are completely unnecessary if all they are doing is passing data along to some other function.

class A():

    def __init__(self, msg, term_print)
        self.msg = msg
        self.term_print = term_print

    def a_0(self):
        return self.a_1()

    def a_1(self):
        return self.a_2()

    def a_2(self):
        print(msg, self.term_print)
like image 66
Brendan Abel Avatar answered Sep 17 '22 12:09

Brendan Abel