5

Python newbie here, running 2.7.

I am trying to create a program that uses a function to generate text, and then outputs the function-generated text to a file.

When just printing the function in powershell (like this: http://codepad.org/KftHaO6x), it iterates, as I want it to:

def writecode (q, a, b, c):
    while b < q:
        b = b + 1
        print "v%d_%d_%d = pairwise (caps[%d],sals[%d],poss[%d],poss[%d],poss[%d],pos_range)" %(a,b,c,a,a,a,b,c)
        print "votes%d_%d.append(v%d_%d_%d)" % (b,c,a,b,c,)
        print "v%d_%d_%d = pairwise (caps[%d],sals[%d],poss[%d],poss[%d],poss[%d],pos_range)" %(a,c,b,a,a,a,c,b)
        print "votes%d_%d.append(v%d_%d_%d)" % (c,b,a,c,b)

writecode (5,1,0,4)

When trying to output the function into a file (like this: http://codepad.org/8GJpp9QY), it only gives 1 value, i.e. does not iterate:

def writecode (q, a, b, c):
    while b < q:
        b = b + 1
        data_to_write = "v%d_%d_%d = pairwise (caps[%d],sals[%d],poss[%d],poss[%d],poss[%d],pos_range)" %(a,b,c,a,a,a,b,c)
        data_to_write_two = "votes%d_%d.append(v%d_%d_%d)" % (b,c,a,b,c,)
        data_to_write_three = "v%d_%d_%d = pairwise (caps[%d],sals[%d],poss[%d],poss[%d],poss[%d],pos_range)" %(a,c,b,a,a,a,c,b)
        data_to_write_four = "votes%d_%d.append(v%d_%d_%d)" % (c,b,a,c,b)
        return data_to_write
        return data_to_write_two
        return data_to_write_three
        return data_to_write_four

x = writecode (5,1,0,4)

out_file = open("code.txt", "a")
out_file.write(x)
out_file.close()

Why is this, and how can I make the output function iterate (like it does with print)?

Hamish
  • 22,860
  • 8
  • 53
  • 67
user1569317
  • 2,436
  • 3
  • 14
  • 17
  • 5
    The `return` statement **terminates** the function (like in most (all?) languages): http://docs.python.org/reference/simple_stmts.html#the-return-statement – Felix Kling Aug 01 '12 at 20:17
  • 3
    Thanks to Hamish for putting the actual code here in the question... – ldx.a.ldy.c Aug 01 '12 at 20:18
  • 2
    Stepping back a bit, you seem to be using code generation. Why don't you just execute the code directly? Or say something about your larger task? – Francis Avila Aug 01 '12 at 20:23
  • I am using code generation because I "need" a lot of code which only slightly differs. Quotation marks = caveat that I'm a programming rookie and could be totally wrong. The larger task is to try to simulate pairwise voting. If you're not familiar with the term, it basically means calculating whether Actor 1 votes for Actor 2 in comparison to Actor 3. So if you have 15 actors, you need to calculate 1,2...n's preference for 2 vs. 3, 3 vs. 4, etc. Hence the many lines of slightly different code. Here is what I have so far if you are interested: http://codepad.org/Ey5C4Rd7 Advice is welcome. – user1569317 Aug 01 '12 at 20:43
  • A specific thing I noticed about the code was that it looks like the many `votes#_#` lists you're keeping could be made into single list of lists (or perhaps dictionary of dictionaries) data structure that was accessed via `votes[#][#]`. Generally speaking, when you find yourself writing lots of almost identical code snippets it's often useful to ask "Of what general case are each one of these just one instance?" This often will allow you to design a function or method that handles them all if it's provided with the right data via arguments in the call or stored object attributes. – martineau Aug 02 '12 at 02:26

4 Answers4

5

Your issue is that you can only return once from a function:

def test():
    return 1
    return 2

x = test()
# x is now 1

You'll want to either use yield or return a tuple of values:

def this_will_work():
    yield 1
    yield 2

x = list(this_will_work()) # See below for why we are using list
# x is now [1, 2]

def so_will_this:
    return 1, 2

x = so_will_this()
# x is now (1, 2)

Using yield turns your function into a generator that can be iterated over - if you are only interested in all the final values you can use list to turn the generator into a list of all the values your generator yields. Alternately you can loop over it using for ... in.

Sean Vieira
  • 155,703
  • 32
  • 311
  • 293
2

In the version you're using to write the file, the function returns (via the first return statement) after the first iteration of the while loop. Based on what you have you might want something like this:

def writecode (q, a, b, c):
    results = []
    while b < q:
        b = b + 1
        results.append("v%d_%d_%d = pairwise (caps[%d],sals[%d],poss[%d],poss[%d],poss[%d],pos_range)" % (a,b,c,a,a,a,b,c))
        results.append("votes%d_%d.append(v%d_%d_%d)" % (b,c,a,b,c,))
        results.append("v%d_%d_%d = pairwise (caps[%d],sals[%d],poss[%d],poss[%d],poss[%d],pos_range)" % (a,c,b,a,a,a,c,b))
        results.append("votes%d_%d.append(v%d_%d_%d)" % (c,b,a,c,b))
        results.append("")
    return "\n".join(results)

x = writecode (5,1,0,4)

out_file = open("code.txt", "a")
out_file.write(x)
out_file.close()

Which works by accumulating each line of output into a list and then returns the single string with all the results joined together with newlines and a trailing newline.

martineau
  • 119,623
  • 25
  • 170
  • 301
  • Thank you, the code you provided exactly fits what I needed. Much appreciated! – user1569317 Aug 01 '12 at 20:47
  • @user1569317: You're welcome. FWIW there are probably easier ways to accomplish what you're doing or at least make it a little more readable. All I did was show you a relatively simple way to get what you had working. – martineau Aug 02 '12 at 02:03
1

return immediately exists the function:

return leaves the current function call with the expression list (or None) as return value.

You need to build the string as you go (or, for better performance, create a list of values and join at the end).

Hamish
  • 22,860
  • 8
  • 53
  • 67
0

A slight variation; I have used new-style string formatting with named fields, which should be easier to read.

Do be aware that you are generating some duplicated blocks (every case in which b==c appears twice in your output).

from textwrap import dedent

codeblock = dedent("""\
    v{a}_{b}_{c} = pairwise (caps[{a}],sals[{a}],poss[{a}],poss[{b}],poss[{c}],pos_range)
    votes{b}_{c}.append(v{a}_{b}_{c})
    v{a}_{c}_{b} = pairwise (caps[{a}],sals[{a}],poss[{a}],poss[{c}],poss[{b}],pos_range)
    votes{c}_{b}.append(v{a}_{c}_{b})
""")

def get_code (q, a, b, c):
    return ''.join(codeblock.format(a=a, b=b, c=c) for b in xrange(b, q))

def main():
    with open('code.txt', 'a') as outf:
        outf.write(get_code(5,1,0,4))

if __name__=="__main__":
    main()
Hugh Bothwell
  • 55,315
  • 8
  • 84
  • 99