views:

255

answers:

6

I do scientific programming, and often want to show users prompts and variable pairs, let them edit the variables, and then do the calulations with the new variables. I do this so often, that I wrote a wxPython class to move this code out of the main program. You set up a list for each variable with the type of the variable (string, float, int), the prompt, and the variable's current value. You then place all of these lists in one big list, and my utility creates a neatly formated wxPython panel with prompts and the current values which can be edited.

When I started, I only had a few variables, so I would write out each variable.

s='this is a string'; i=1; f=3.14
my_list=[ ['s','your string here',s], ['i','your int here',i], ['f','your float here'],]
input_panel = Input(my_list)

 # the rest of the window is created, the input_panel is added to the window, the user is
 # allowed to make  choices, and control returns when the user hits the calculate button

s,i,f = input_panel.results()     # the .results() function returns the values in a list

Now I want to use this routine for a lot of variables (10-30), and this approach is breaking down. I can create the input list to the function over multiple lines using the list.append() statements. When the code returns from the function, though, I get this huge list that needs to be unpacked into the right variables. This is difficult to manage, and it looks like it will be easy to get the input list and output list out of sync. And worse than that, it looks kludgy.

What is the best way to pass lots of variables to a function in Python with extra information so that they can be edited, and then get the variables back so that I can use them in the rest of the program?

If I could pass the variables by reference into the function, then users could change them or not, and I would use the values once the program returned from the function. I would only need to build the input list over multiple lines, and there wouldn't be any possiblity of the input list getting out of sync with the output list. But Python doesn't allow this.

Should I break the big lists into smaller lists that then get combined into big lists for passing into and out of the functions? Or does this just add more places to make errors?

+15  A: 

The simplest thing to do would be to create a class. Instead of dealing with a list of variables, the class will have attributes. Then you just use a single instance of the class.

Ned Batchelder
cant count the amount of "search properties" classes I have made in my life. +1
Matt Briggs
Definitely start with a class. You may then spot some data that can be split into multiple classes, or behaviour that can be moved into the class. But creating the argument object is the first step.
Douglas Leeder
As someone who works in a company that does scientific software, I also think that this is the right approach. By passing a whole bunch of variables around, you're basically giving up on modeling your problem domain. Instead you could be constructing a class hierarchy that embodies your problem in a more logical sense as a collection of objects. The 10-30 variable function calls are a typical sign of an amateur programmer coming from an academic science background.
Kamil Kisiel
A: 

if you have a finite set of these cases, you could write specific wrapper functions for each one. Each wrapper would do the work of building and unpacking lists taht are passed to the internal function.

sean riley
+1  A: 

If I could pass the variables by reference into the function, then users could change them or not, and I would use the values once the program returned from the function.

You can obtain much the same effect as "pass by reference" by passing a dict (or for syntactic convenience a Bunch, see http://code.activestate.com/recipes/52308/).

Alex Martelli
+8  A: 

There are two decent options that come to mind.

The first is to use a dictionary to gather all the variables in one place:

d = {}
d['var1'] = [1,2,3]
d['var2'] = 'asdf'
foo(d)

The second is to use a class to bundle all the arguments. This could be something as simple as:

class Foo(object):
    pass
f = Foo()
f.var1 = [1,2,3]
f.var2 = 'asdf'
foo(f)

In this case I would prefer the class over the dictionary, simply because you could eventually provide a definition for the class to make its use clearer or to provide methods that handle some of the packing and unpacking work.

Doug
+2  A: 

To me, the ideal solution is to use a class like this:

>>> class Vars(object):
...     def __init__(self, **argd):
...             self.__dict__.update(argd)
...
>>> x = Vars(x=1, y=2)
>>> x.x
1
>>> x.y
2

You can also build a dictionary and pass it like this:

>>> some_dict = {'x' : 1, 'y' : 2}
>>> #the two stars below mean to pass the dict as keyword arguments
>>> x = Vars(**some_dict)  
>>> x.x
1
>>> x.y
2

You may then get data or alter it as need be when passing it to a function:

>>> def foo(some_vars):
...     some_vars.z = 3 #note that we're creating the member z
...
>>> foo(x)
>>> x.z
3
Jason Baker
A: 
  1. I would recommend using a dictionary or a class to accumulate all details about your variables
    • value
    • prompt text
  2. A list to store the order in which you want them to be displayed
  3. Then use good old iteration to prepare input and collect output

This way you will only be modifying a small manageable section of the code time and again. Of course you should encapsulate all this into a class if your comfortable working with classes.

"""Store all variables
"""
vars = {}
"""Store the order of display
"""
order = []

"""Define a function that will store details and order of the variable definitions
"""
def makeVar(parent, order, name, value, prompt):
    parent[name] = dict(zip(('value', 'prompt'), (value, prompt)))
    order.append(name)

"""Create your variable definitions in order
"""
makeVar(vars, order, 's', 'this is a string', 'your string here')
makeVar(vars, order, 'i', 1, 'your int here')
makeVar(vars, order, 'f', 3.14, 'your float here')

"""Use a list comprehension to prepare your input
"""
my_list = [[name, vars[name]['prompt'], vars[name]['value']] for name in order]
input_panel = Input(my_list)

out_list = input_panel.results();
"""Collect your output
"""
for i in range(0, len(order)):
    vars[order[i]]['value'] = out_list[i];
your commenting method is non-standard, please read pep-8.http://www.python.org/dev/peps/pep-0008/
monkut