1

When I write functions in Python, I typically need to pass quite a few variables to the function. Also, output of such functions contains more than a few variables. In order to manage this variables I/O, I resort to the dictionary datatype, where I pack all input variables into a dictionary to inject into a function and then compile another dictionary at the end of the function for returning to the main program. This of course needs another unpacking of the output dictionary.

dict_in = {'var1':var1,
           'var2':var2,
           'varn':varn}    

def foo(dict_in):
    var1 = dict_in['var1']
    var2 = dict_in['var2']
    varn = dict_in['varn']

    """ my code """

    dict_out = {'op1':op1,
                'op2':op2,
                'op_m':op_m}
    return dict_out

As the list of variables grows, I suspect that this will be an inefficient approach to deal with the variables I/O.

Can someone suggest a better, more efficient and less error-prone approach to this practice?

bluetooth
  • 529
  • 6
  • 20
  • *better, more efficient and less error-prone approach* --> **pythonic way** – Haris Apr 13 '18 at 21:37
  • Packaging into an object is necessary for multiple return values, but unless the input values are very closely related, forcibly grouping them in an object is just going to complicate things, and isn't necessarily conceptually sounds. – Carcigenicate Apr 13 '18 at 21:37
  • @Carcigenicate Python allows multiple return values without packaging... I'm not following your comment. – TemporalWolf Apr 13 '18 at 21:48
  • 1
    @TemporalWolf That's just implicit tuple packaging afaik. It's only 1 return value, that one return just happens to be a tuple with multiple elements. – Carcigenicate Apr 13 '18 at 21:53
  • @TemporalWolf `def f(): return 1, 2 print(type(f()))` prints `` – Carcigenicate Apr 13 '18 at 21:56
  • 1
    Actually, it's not even implicit. From the second answer of the dupe target, a comma is all that's required to create a tuple, so my example is just explicitly creating a tuple then returning it. – Carcigenicate Apr 13 '18 at 21:58

2 Answers2

3

You could take advantage of kwargs to unpack named variables

def foo(**kwargs):
    kwargs['var1'] = do_something(kwargs['var1'])
    ...
    return kwargs
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
bphi
  • 3,115
  • 3
  • 23
  • 36
3

If you find yourself writing a lot of functions that act on the same data, one better way would be using classes to contain your data.

class Thing:
    def __init__(self, a, b, c):
        var_1 = a
        var_2 = b
        var_3 = c

    # you can then define methods on it

    def foo(self):
        self.var_1 *= self.var_2

# and use it
t = Thing(1, 2, 3)
t.foo()
print(t.var_1)

There are a number of methods of creating these in an easier way. Some of them include:

attrs:

>>> @attr.s
... class SomeClass(object):
...     a_number = attr.ib(default=42)
...     list_of_numbers = attr.ib(default=attr.Factory(list))
...
...     def hard_math(self, another_number):
...         return self.a_number + sum(self.list_of_numbers) * another_number

namedtuples

>>> Point = namedtuple('Point', ['x', 'y'])
>>> p = Point(11, y=22)     # instantiate with positional or keyword arguments
>>> p.x + p.y               # fields accessible by name
33

Dataclasses

These are not in python yet, but will be added in 3.7. I am adding them in here here because they will likely be the tool of choice in the future.

Azsgy
  • 3,139
  • 2
  • 29
  • 40
  • +1 for namedtuple. It is easy to use and it holds the data in a immutable sequence type that you don't have to worry about. Perfect for storing data in a "I don't care what this is, I just want to get the data across" way. For geometric points I would use a class (maybe `__add__` would be helpful, but for stuff like mailing addresses, url, etc., I think namedtuples are definitely underrated. (I was about to type up a namedtuple answer but you got it before I can, so I'm going to put my praise rant here instead.) – T Tse Apr 13 '18 at 21:58