1

I have a list of labels, e.g., ['A', 'B', 'C', 'D'], and a list of suffixes, e.g., ['left', 'right'], and I would like to create a pair of strings which combine the two lists systematically, e.g., lstr = 'Aleft, Bleft, Cleft, Dleft, ' and rstr = 'Aright, Bright, Cright, Dright, '. I tried to write a set of nested for loops in order to do this, like so:

labels = ['A', 'B', 'C', 'D']
suffix = ['left', 'right']

# This is what I actually want to do, and it works exactly as desired inside
# of the for loop, but I can't pass the result back outside of the for loop
lstr, rstr = '', ''
for lr, s in zip([lstr, rstr], suffix):
    for lbl in labels:
        lr += lbl + s + ", "
    print(lr)

print(lstr)
print(rstr)

# Just for comparison, this is syntactically very similar to what I want to
# do, and it does work as desired
llist, rlist = [], []
for lr, s in zip([llist, rlist], suffix):
    for lbl in labels:
    lr.append(lbl + s)

print(llist)
print(rlist)

This code snippet yields the following 6-line result:

Aleft, Bleft, Cleft, Dleft, 
Aright, Bright, Cright, Dright, 


['Aleft', 'Bleft', 'Cleft', 'Dleft']
['Aright', 'Bright', 'Cright', 'Dright']

Essentially, the two strings that I want are indeed being constructed correctly inside of the first pair of nested for loops, resulting in the first two correct/desired lines which you can see at the beginning of the output. However, the for loops fail to "pass the result back up" to the higher-level lstr, rstr variables on the outside of the for loops, with the result that the next two lines of output, produced by the print(lstr) and print(rstr) statements, are blank.

Just for comparison, the second pair of nested for loops are also a syntactically very similar construction to the first, except that instead of building a string, one builds a list. Unlike the first example, this construction works as expected--the reference to the resulting list is passed back up above the for loops, and printing out the result leads to the final two lines in the output.

I suspect that the difference in behavior between these two nearly identical constructions is somehow related to the fact that Python objects technically are neither pass by value nor pass by reference. However, I'm not quite sure what to actually do about it.

So, therefore, my question: what is the most succinct, correct and/or "Pythonic" way of restructuring the first pair of nested for loops, so that I can get the desired result?

stachyra
  • 4,423
  • 4
  • 20
  • 34

4 Answers4

2

Maybe this:

labels = ['A', 'B', 'C', 'D']
suffixes = ['left', 'right']

result = (', '.join(label+suffix for label in labels) for suffix in suffixes)

for line in result:
    print line
Juan Lopes
  • 10,143
  • 2
  • 25
  • 44
1

You could use a nested list comprehension to make a list of lists for you. This is extensible in the sense that labels and suffix could have any dimensions.

>>> labels = ['A', 'B', 'C', 'D']
>>> suffix = ['left', 'right']
>>> [[''.join([i,j]) for i in labels] for j in suffix]
[['Aleft', 'Bleft', 'Cleft', 'Dleft'], ['Aright', 'Bright', 'Cright', 'Dright']]

After re-reading your post, it looks like you may have wanted two strings, concatenated by commas. If that is correct, you can modify the above to:

>>> [', '.join(''.join([i,j]) for i in labels) for j in suffix]
['Aleft, Bleft, Cleft, Dleft', 'Aright, Bright, Cright, Dright']
Cory Kramer
  • 114,268
  • 16
  • 167
  • 218
0

Lists are mutable; strings aren't, so you reference to another object rather that modifying the object itself.

Consider this:

A = [1, 2, 3, 4]
B = A
B.append(5)

Right now both B and A point to the same object, which is the list composed of the numbers 1 to 5.

But this:

S1 = "1, 2, 3, 4"
S2 = S1
S2 += ", 5"

By doing this you don't actually change the string, but rather create a new one, "1, 2, 3, 4" + ", 5", and thus S2 points to a new object and not to the same object as S1, which is still pointing to "1, 2, 3, 4".

More information:

Immutable vs Mutable types

https://docs.python.org/2/reference/datamodel.html

Community
  • 1
  • 1
unddoch
  • 5,790
  • 1
  • 24
  • 37
0

You can assign multiple strings at once, so if you know it'll just be lstr and rstr you can do the following with list comprehension:

lstr, rstr = [', '.join('{}{}'.format(i, j) for j in suffixes) for i in labels]

Note the placement of iteration; for j in suffixes is within the scope of the ''.join() call but for i in labels is just in the list comprehension, which might at first seem intuitively backwards (or at least it 'feels' backwards to me, but it isn't).