2

I find myself in a need of working with functions and objects who take a large number of variables.

For a specific case, consider a function from a separated module which takes N different variables, which are then pass them on to newly instanced object:

def Function(Variables): 
    Do something with some of the variables
    object1 = someobject(some of the variables)
    object2 = anotherobject(some of the variables, not necessarily as in object1)

While i can just pass a long list of variables, from time to time i find myself making changes to one function, which requires making changes in other functions it might call, or objects it might create. Sometimes the list of variables might change a little.

Is there a nice elegant way to pass a large group of variables and maintain flexibility?

I tried using kwargs in the following way:

def Function(**kwargs):

    Rest of the function

and calling Function(**somedict), where somedict is a dictionary has keys and values of all the variables i need to pass to Function (and maybe some more). But i get an error about undefined global variables.

Edit1:

I will post the piece of code later since i am not at home or the lab now. Till then i will try to better explain the situation.

I have a molecular dynamics simulation, which take few dozens of parameters. Few of the parameters (like the temperature for example) need to be iterated over. To make good use of the quad core processor i ran different iterations in parallel. So the code starts with a loop over the different iteration, and at each pass send that parameters of that iteration to a pool of workers (using the multiprocessing module). It goes something like:

P = mp.pool(number of workers) # If i remember correctly this line 
for iteration in Iterations:
     assign values to parameters
     P.apply_async(run,(list of parameters),callback = some post processing)
P.close()
P.join()

The function run takes the list of parameters and generates the simulation objects, each take some of the parameters as their attributes.

Edit2:

Here is a version of the problematic function. **kwargs contain all the parameters needed by the 'sim','lattice' and 'adatom'.

def run(**kwargs): 
"""'run' runs a single simulation process.
j is the index number of the simulation run.
The code generates an independent random seed for the initial conditios."""
scipy.random.seed()
sim = MDF.Simulation(tstep, temp, time, writeout, boundaryxy, boundaryz, relax, insert, lat,savetemp)
lattice = MDF.Lattice(tstep, temp, time, writeout, boundaryxy, boundaryz, relax, insert, lat, kb, ks, kbs, a, p, q, massL, randinit, initvel, parangle,scaletemp,savetemp,freeze)
adatom = MDF.Adatom(tstep, temp, time, writeout, boundaryxy, boundaryz, relax, insert, lat, ra, massa, amorse, bmorse, r0, z0, name, lattice, samplerate,savetemp,adatomrelax)                

bad = 1 
print 'Starting simulation run number %g\nrun' % (j+1)    
while bad is 1:
    # If the simulation did not complete successfuly, run it again.
    bad = sim.timeloop(lattice,adatom1,j)
print 'Starting post processing'
# Return the temperature and adatomś trajectory and velocity
List = [j,lattice.temp , adatom1.traj ,adatom1.velocity, lattice.Toptemp,     lattice.Bottomtemp, lattice.middletemp, lattice.latticetop]
return  List        
Mickey Diamant
  • 657
  • 1
  • 9
  • 17
  • 1
    You're on the right track, use either *args or **kwargs, so there's probably something wrong in your code. Please post it so that we can see what's going on. – aquavitae Mar 09 '12 at 09:50

3 Answers3

8

The cleanest solution is not using that many parameters in a function at all.

You could use set methods or properties to set each variable separately, storing them as class members and being used by the functions inside that class.

Those functions fill the private variables and the get methods can be used to retrieve those variables.

An alternative is to use structures (or classes without functions) and this way create a named group of variables.

Michel Keijzers
  • 15,025
  • 28
  • 93
  • 119
  • 6
    Long parameter lists is a "code smell" or antipattern, and there are documented solutions to resolve them (usually involving structs or classes). – PaulMcG Mar 09 '12 at 09:58
1

Putting *args and/or **kwargs as the last items in your function definition’s argument list allows that function to accept an arbitrary number of anonymous and/or keyword arguments.

You would use *args when you're not sure how many arguments might be passed to your function.

avasal
  • 14,350
  • 4
  • 31
  • 47
1

You also can group parameters in tuples to make a structure.

This is not as elegant as using structures or get/set methods but can be applied mostly easily in existing applications without too much rework.

Of course only related parameters should be grouped in a tuple.

E.g. you could have a function passed as

value = function_call((car_model, car_type), age, (owner.name, owner.address, owner.telephone))

This does not reduce the number of parameters but adds a bit more structure.

Michel Keijzers
  • 15,025
  • 28
  • 93
  • 119