views:

220

answers:

4

I find it very useful to be able to create new variables during runtime and create a dictionary of the results for processing later, i.e. writing to a file:

myDict = {}
for i in range (1,10):
    temp = "variable"+str(i) 
    vars()[temp] = myFunctionThatReturnsData() # variable1= data1, variable2 = data2,etc.
    myDict[temp] = vars(temp)

which creates the dictionary entry [result1:data1] which i can call with myDict[result1]. I have been using vars() without really understanding what I'm doing. I take it vars() returns a dictionary with the local variables(?), and

vars()[x] = y

creates a new dictionary entry of [x:y] ?

I have a script where I pass in a dictionary prepared with {input1:data1,input2:data2}, and i use this method to iterate through all the values, store all the results, and output it to a file. This bit of code is inside a function within a class, and is working.

My source of confusion is that I have read various posts on how locals() shouldn't be messed with, and how vars() is equivalent(?) to locals(), or globals()..

So my question is (at least) two-fold:

1.What exactly does vars(),or in particular, vars()[x] = y do,

2.What the scope of this dictionary is (what I need to keep in mind as I write bigger programs

3.Whether this is good programming practice.

Thanks in advance!

+2  A: 

I can answer number 3: this isn't good programming practice. I don't exactly see what you are trying to accomplish, but I am sure there is a more elegant way of doing it without using locals() (which is the same as vars() according to help(vars) in the interactive Python shell).

Otto Allmendinger
+2  A: 

From the help for vars,

vars(...) vars([object]) -> dictionary

Without arguments, equivalent to locals().
With an argument, equivalent to object.__dict__.

You are using it without vars, so let's look at the help for locals()

locals(...) locals() -> dictionary

Update and return a dictionary containing the current scope's local

variables.

So this answers you first two questions. vars() returns a dictionary to the local variables that is indexed by the name of the variable as a string. The scope is local.

I'm not sure about the third question, but it does seem like kind of a hack which isn't a good sign. I guess if you're careful about using this only in the correct scope you can get by with it.

Justin Peel
i got so caught up in my confusion i forgot to ask for help(). Thanks,
PPTim
+3  A: 

Do this instead. It's simpler.

myDict = {}
for i in range (1,10):
    temp = "variable"+str(i) 
    myDict[temp] = myFunctionThatReturnsData() # variable1= data1, variable2 = data2,etc.

That's all you ever need to do.

The results will be myDict['variable1'] through myDict['variable9']

You rarely need vars() or locals(). Just stop using them and use ordinary variables and ordinary dictionaries. Try to avoid things you don't understand and stick to the simple, obvious stuff.

S.Lott
Now that I think about why I used vars(), it was probably because it felt good to be able to call the variables right off IDLE when I tested the code line by line. Thanks for the answer!
PPTim
+2  A: 

The pythonic way to create a sequence of variables

If you want a sequence of variables, create a sequence. Instead of trying to create independent variables like:

variable0
variable1
variable2
variable3

You should look at creating a list. This is similar to what S.Lott is suggesting (S.Lott usually has good advice), but maps more neatly onto your for loop:

sequence = []
for i in xrange(10):
    sequence.append(function_that_returns_data())

(Notice that we don't even need the i in the for loop. We're just trying to get 10 passes.)

Then your data will be available as:

sequence[0]
sequence[1]
sequence[2]
sequence[3]
[...]
sequence[9]

As an added bonus, you can do:

for datum in sequence:
    process_data(datum)

At first, you may twitch at having your sequence start at 0. You can go through various contortions to have your actual data start at 1, but it's more pain than it's worth. I recommend just getting used to having zero-based lists. Everything is built around them, and they start to feel natural pretty quickly.

vars() and locals()

Now, to answer another part of your question. vars() (or locals()) provides low level access to variables created by python. Thus the following two lines are equivalent.

locals()['x'] = 4
x = 4

The scope of vars()['x'] is exactly the same as the scope of x. One problem with locals() (or vars()) is that it will let you put stuff in the namespace that you can't get out of the namespace by normal means. So you can do something like this: locals()[4] = 'An integer', but you can't get that back out without using locals again, because the local namespace (as with all python namespaces) is only meant to hold strings.

>>> x = 5
>>> dir()
['__builtins__', '__doc__', '__name__', 'x']
>>> locals()[4] = 'An integer'
>>> dir()
[4, '__builtins__', '__doc__', '__name__', 'x']
>>> x
5
>>> 4
4
>>> locals()[4]
'An integer'

Note that 4 does not return the same thing as locals()[4]. This can lead to some unexpected, difficult to debug problems. This is one reason to avoid using locals(). Another is that it's generally a lot of complication just to do things that python provides simpler, less error prone ways of doing (like creating a sequence of variables).

jcdyer
I think `vars()['x'] = 4` and `x = 4` are actually equivalent only some of the time. If you do this in a function, and don't set `x` elsewhere, and the function is optimized by the compiler, then normal lookup of `x` later in the function (ie, `y = x + 2`) won't work. I think the compiler caches which variables it can see (were possibly defined at compile time), and doesn't take into account this kind of shenanigan. If you add an exec statement to your function, then the compiler won't try to optimize the function.
Matt Anderson
thanks for the thoughtful answer, for datum in sequence: function(datum) looks very useful. There's a lot here that I learned today, thanks!
PPTim
To find more information on that construct, look up "python iterators"
jcdyer