2
\$\begingroup\$
def fib_m_through_n(m, n):
    """(number, number) -> list

    A function which returns a list containing the mth through the nth
    fibonacci numbers.

    SHOULD BE MODIFIED IN THE FUTURE so it will remember values already computed,
    so when it is called again it reuses that information
    """
    fib_list = [0, 1, 1]
    a, b, c = 1, 1, 0  #why can I do three variable assignments here...
    count = 0
    while count <= n:
        c = a + b; a = b; b = c #...but not here?
        fib_list.append(c)
        count += 1
    print(fib_list[m : n])

fib_m_through_n(0, 17)

I have a few questions about this:

The first is general: are there any clear pythonic violations here, any obvious improvements to style?

The second is included in some of the comments. Why can I get away with assigning three variables in one line here: a, b, c = 1, 1, 0

but not here: c = a + b; a = b; b = c

If I try to do this: c, a, b = a + b, b, c

Then I get an incorrect output.

After analyzing the mistake and trying to imagine how things look from the function's POV, it still seems to me like doing three variable assignments in a row should work, assuming that when the function runs it just reads this line from left to right.

Finally, as noted in the section just after the docstring, I want to turn this into a generator. I have read about generators and understand them a bit, but I don't know how to implement one here.

\$\endgroup\$

2 Answers 2

4
\$\begingroup\$

Of course not.

If you do c, a, b = a + b, b, c, this is what you get

a, b, c = a0, b0, c0
c, a, b = a + b, b, c 
## c become a0 + b0
## a becomes b0
## b becomes c0

I'm pretty sure this is not your intention.

In tuple assignment, the right tuple is evaluated first and then assigned to the left tuple. I'm guessing you thought it's done in order. Sorry, but no.

You can instead eliminate the variable c and do this

a, b = a0, b0
a, b = b, a + b
## a becomes b0
## b becomes a0 + b0

UPDATE

This is what is done by the machine, imagine the left values as an array of variables waiting to be assigned values that are on the left. Like this

[c, a, b] = [a + b, b, c] 

Imagine the right values as an array of expression waiting to be evaluated. The right one gets evaluated first, then one by one the left variables takes the right values. No assignment is done until the left expressions are evaluated.

\$\endgroup\$
2
  • \$\begingroup\$ Thanks for your reply. As I understand it, that actually was my intention. I want c to be a + b, then I want a to equal b and b to equal c. This may not be pretty, but it will shuffle through the Fibonacci sequence. And if the right tuple evaluates first, doesn't that just mean that a + b will evaluate and assign to c, then b will assign to a, then c will assign to b? \$\endgroup\$ Sep 11, 2013 at 0:43
  • \$\begingroup\$ @TrentFowler read my update \$\endgroup\$
    – Olayinka
    Sep 11, 2013 at 1:22
1
\$\begingroup\$

Rather than looping with while and a count variable, it is more pythonic to write for _ in range(n + 1). Also worth commenting that you are storing the same values in two places - in fib_list and in a, b, c.

fibonacci = [0, 1]
for _ in range(n + 1):
    fibonacci.append(fibonacci[-2] + fibonacci[-1])
print (fibonacci[m:n])

To convert this into a generator is fairly straightforward:

def fibonacci(m, n):
   a, b = 0, 1
   for _ in xrange(m):
       a, b = b, a + b
   for _ in xrange(n - m):
       yield a
       a, b = b, a + b
print (list(fibonacci(0, 17)))

If, as you say in your docstring, you want to hold values that have already been calculated, then making a generator does not particularly help you; instead you need to make a class in which you can store the values. In this case it will be handy to overload the slice operators so you can treat Fibonacci objects as if they were lists.

class Fibonacci:
    def __init__(self):
        self.sequence = [0, 1]
    def __getitem__(self, key):
        if isinstance(key, int):
            stop = key + 1
        elif isinstance(key, slice):
            stop = key.stop
        while len(self.sequence) < stop:
            self.sequence.append(self.sequence[-2] + self.sequence[-1])
        return self.sequence[key]
f = Fibonacci()
print f[7:17]
\$\endgroup\$
3
  • \$\begingroup\$ Thanks very much, I will study this code carefully. If you didn't know, I think xrange was replaced in python 3, because I had to change it to 'range' when I ran the code you gave me. The output was still correct, though. \$\endgroup\$ Sep 11, 2013 at 0:50
  • \$\begingroup\$ Also, you say that if I want the function to remember values then a generator won't be much help. My understanding of generators was that they could remember where they stopped when they passed control to something else. How is that different from storing values? \$\endgroup\$ Sep 11, 2013 at 1:01
  • \$\begingroup\$ Yes, actually the generator does store values too. But with the class, you could call f[0:17] and it will calculate and store the first 17 values of the sequence; and then later write f[0:25] and it will use the 17 values it has already calculated. With the generator you could write f = fibonacci(0, 100) and get values of the sequence with f.next(), so you could end up being able to use it in a similar way. \$\endgroup\$
    – Stuart
    Sep 11, 2013 at 7:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.