3

This question is related to my earlier question here: python update outer passed value from within a for loop.

Coming from a Perl background it has never been a problem to pass a variable by reference and update the value from within a child scope as in the for-loop example below:

#!/usr/bin/perl
my ($str1,$str2) = ('before','before');
print "before - str1:'$str1', str2:'$str2'\n";

foreach my $str($str1,$str2){$str = 'after'}
print "after - str1:'$str1', str2:'$str2'\n";

I understand in Python this is not possible as variables are imported by-value rather than by-reference, the only solution I've found within Python so far which achieves exactly what I need is:

def changeValue(str):
  return 'after'

str1 = 'before'
str2 = 'before'
for s in str1,str2: print s

str1 = changeValue(str1)
str2 = changeValue(str2)
for s in str1,str2: print s

Although not ideal this would be OK if I could make the function 'changeValue' calls from within a for-loop - rather than calling them individually as above - but then I am back to the original "can't pass a variable by reference" problem.

I am sure that there must be a simple and less convoluted Pythonic way to achieve what I'm after?

Community
  • 1
  • 1
evillen
  • 103
  • 2
  • 8
  • 2
    what's the real difference between `s1 = change(s1)` and `change(s1)`? Note that python *does* pass by reference, it's just that assignment creates a new reference and some types (int, str, ...) are immutable, so there is no real way to change the object that the reference references ... – mgilson Sep 06 '12 at 15:09
  • BTW: why do you keep showing example code rather than your real code? Real code is always the better way to get useful help. – Ned Batchelder Sep 06 '12 at 15:14
  • Ned - my previous linked question contains the use-case or 'real' code – evillen Sep 06 '12 at 15:38
  • mgilson, thanks for the tip `change(s1)` – evillen Sep 06 '12 at 15:43

2 Answers2

5

In python, the way to do something like this is almost certainly to use a dictionary to store your data:

d = {'s1':'before','s2':'before'}

for k in d.keys():
    print d[k]

for k in d.keys():
    d[k] = 'after'
    print d[k]

There are other solutions (modifying the globals dictionary works, but is HIGHLY DISCOURAGED AS IT LEADS TO CODE WHICH IS ALMOST IMPOSSIBLE TO READ/DEBUG). Perhaps if we knew the actual use-case for this, we might be able to provide some better advice.

mgilson
  • 300,191
  • 65
  • 633
  • 696
  • 1
    Don't even mention `globals`. – Ned Batchelder Sep 06 '12 at 15:15
  • @NedBatchelder -- why not? If a user wants to shoot their legs off, it's up to them ... (and as far as I know, the standard even permits modification of `globals` as opposed to `locals`.) – mgilson Sep 06 '12 at 15:16
  • I just don't see why you would answer with, "here is the good way to do it, and btw, here's the bad way, DON'T DO IT!" – Ned Batchelder Sep 06 '12 at 15:18
  • 2
    because the good way doesn't actually answer the question. The question is "how do I do ..." and the good "answer" is "you don't, but here's something else which will *probably* work for you ..." which isn't really an answer as far as I'm concerned. As I see it, the answer is "here's how you do it, but really, doing it at all is a bad idea. Here's a better way to think about the problem ..." – mgilson Sep 06 '12 at 15:23
  • 2
    The poster would have discovered the bad way on their own eventually. But now, they know to avoid it. – abought Sep 06 '12 at 16:13
2

The solution to this is to stop using independent variables, and instead store them in a dictionary. For example:

def changeValue(str):
  return 'after'

data = {
    'str1': 'before',
    'str2': 'before',
}

for k, v in data.iteritems():
    print k, v

# as you had it:
data['str1'] = changeValue(str1)
data['str2'] = changeValue(str2)

# with a loop:
for key in ['str1', 'str2']:
    data[key] = changeValue(data[key])

# or a nicer way:
for k, v in data.iteritems():
    data[k] = changeValue(v)

for k, v in data.iteritems():
    print k, v
Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662