How does this implementation of swap works in Python? - python

def fibonacci(num):
a=0
b=1
for i in range(num):
a, b=b, a+b
print a
How does the line inside the loop works?
Somehow a & b 's values change, can seems to understand how..
EDIT:
For some reason I got confused, thought that the middle exp of b=b is something new...
didn't read it well..
It really is (a,b) = (b, a+b) which is the basic form of swap in python (:

b, a+b creates a tuple
This tuple is unpacked back into a and b

This line a, b = b, a+b is equivalent to (a, b) = (b, a+b), which is a tuple assignment.

The line in question can be more clearly written (through tuple packing on the right side and sequence unpacking on the left side) as:
(a, b) = (b, a + b)
As the assignments to a and b are carried out in parallel, this is exactly the same as:
new_a = b
new_b = a + b
a = new_a
b = new_b

Related

Using arguments as variable names: Why does it solve this problem?

I'm going through some material about functions and I'm writing Python code to make some sense of the pseudocode examples.
The goal is printing the variables after I call the function, then check the new values.
def my_sum(x, y, z):
z = x + y
return x, y, z
A = 1
B = 2
C = 0
my_sum(A, B, C)
my_sum(B, C, A)
my_sum(C, A, B)
my_sum(A, B, C)
my_sum(B, C, A)
print(A, B, C)
My first instinct was to write this procedural approach, but when I do the calling the program won't give the right answer, because A, B and C aren't saving whatever is happening inside the function. So A is always 1, B is 2 and so forth
It turns out when I assign the calling with the arguments, the variables A, B and C receive the new values and they're now keeping it. Finally it prints 21, 8, 13, which is the answer.
A, B, C = my_sum(A, B, C)
B, C, A = my_sum(B, C, A)
C, A, B = my_sum(C, A, B)
A, B, C = my_sum(A, B, C)
B, C, A = my_sum(B, C, A)
How would you implement it or what are the other ways of writing this algorithm?
The thing is I can't wrap my head around why this works at all! It was just a random guess that happened to solve my problem.
python don't have pass by reference option, only pass by value, so your construction is correct, because you returning NEW values (in tuple form), not changing value of variables, that you are passing in.
In Python, an assignment made to a parameter name never affects the value of the name that the caller uses. They are separate names that initially reference the same object, but once the parameter name is assigned something else (like a sum), it references a different object.
Your second attempt works because the function returns a tuple with the values of the three paramater names and your main program unpacks that tuple back into its own names.
However, since the function doesn't need the original value of the third argument, and it doesn't touch the first two arguments, the caller doesn't really need to pass the third argument, and doesn't need to update its own names for the first two arguments... So the function could be designed to only take two arguments and return the new value:
def my_sum(x, y):
return x + y
A = 1
B = 2
C = my_sum(A, B)
A = my_sum(B, C)
B = my_sum(C, A)
C = my_sum(A, B)
A = my_sum(B, C)
Lets start with your function definition and one call.
def my_sum(x, y, z):
z = x + y
return x, y, z
A = 1
B = 2
C = 0
my_sum(A, B, C)
Without the function, this is functionally the same as:
A = 1
B = 2
C = 0
x = A
y = B
z = C
z = x + y
_ = x, y, z
# x, y, and z are discarded since you don't do anything with the return value
You shouldn't expect this to change A, B, or C or if you do you have a misconception about how python variables or names work.
Python variables or names are just a dict with a name pointing to a value.
A = 1
B = 2
C = 0
my_sum(A, B, C)
# this is a very condensed version of what python does in the background
dict_of_globals = dict()
dict_of_globals['A'] = 1
dict_of_globals['B'] = 2
dict_of_globals['C'] = 3
my_sum_local_dict = dict()
my_sum_local_dict['x'] = dict_of_globals['A']
my_sum_local_dict['y'] = dict_of_globals['B']
my_sum_local_dict['z'] = dict_of_globals['C']
# and so on..
Since you only ever assign 1 to dict_of_globals['A'], it would be unreasonable to expect it to be anything other than 1.
The reason this works:
A, B, C = my_sum(A, B, C)
is because you are assigning the return value back to A.
A = x # etc..
# or:
dict_of_globals['A'] = my_sum_local_dict['x']

How do I do multiple things inside one lambda function?

I am learning Python and I want to do multiple things inside one lambda function.
Just for a small example if I want to do addition, subtraction, and multiplication in one function, how do I do that?
I tried to use code like this just to see if it would work and it didn't:
a = 1
b = 2
myFunction = lambda a, b: a + b, b - a, a * b
print(myFunction(a, b))
You can group those operations in a tuple
a = 1
b = 2
myFunction = lambda a, b: (a + b, b - a, a * b)
myFunction(a, b)
output:
(3, 1, 2)
NB. The mistake in you code is that myFunction was a tuple containing your lambda as first element, not a function.
I think there is a slight syntax issue around a lambda returning a tuple.
Use this syntax:
a = 1
b = 2
myFunction = lambda a, b: (a + b, b - a, a * b)
print(myFunction(a, b))

Function as an argument of another function

I'm learning this language hence I'm new with Python. The code is:
def add(a, b):
return a + b
def double_add(x, a, b):
return x(x(a, b), x(a, b))
a = 4
b = 5
print(double_add(add, a, b))
The add function is simple, it adds two numbers. The double_add function has three arguments. I understand what is happening (With some doubts). The result is 18. I can't understand how double_add uses add to function.
The question is, what is the connection between these two functions?
It would be helpful if tell me some examples of using a function as an argument of another function.
Thanks in advance.
In python language, functions (and methods) are first class objects. First Class objects are those objects, which can be handled uniformly.
So, you just pass a method as an argument.
Your method will return add(add(4, 5), add(4, 5)) which is add(9, 9) and it's equals to 18.
A function is an object just like any other in Python. So you can pass it as argument, assign attributes to it, and well maybe most importantely - call it. We can look at a simpler example to understand how passing a function works:
def add(a, b):
return a + b
def sub(a, b):
return a - b
def operate(func, a, b):
return func(a, b)
a = 4
b = 5
print(operate(add, a, b))
print(operate(sub, a, b))
operate(print, a, b)
And this prints out:
9
-1
4 5
That is because in each case, func is assigned with the respective function object passed as an argument, and then by doing func(a, b) it actually calls that function on the given arguments.
So what happens with your line:
return x(x(a, b), x(a, b))
is first both x(a, b) are evaluated as add(4, 5) which gives 9. And then the outer x(...) is evaluated as add(9, 9) which gives 18.
If you would add print(x) in the double_add function you would see that it would print <function add at 0x10dd12290>.
Therefore, the code of double_add is basically the same as if you would do following:
print(add(add(a,b), add(a,b))) # returns 18 in your case
Functions are objects in Python, just like anything else such as lists, strings.. and you can pass them same way you do with variables.
The function object add is passed as an argument to double_add, where it is locally referred to as x. x is then called on each, and then on the two return values from that.
def double_add(x, a, b):
return x(x(a, b), x(a, b))
Let's write it differently so it's easier to explain:
def double_add(x, a, b):
result1 = x(a, b)
result2 = x(a, b)
return x(result1, result2)
This means, take the function x, and apply it to the parameters a and b. x could be whatever function here.
print(double_add(add, a, b))
Then this means: call the double_add function, giving itaddas the first parameter. Sodouble_add`, would do:
result1 = add(a, b)
result2 = add(a, b)
return add(result1, result2)
This is a very simple example of what is called "dependency injection". What it means is that you are not explicitly defining an interaction between the two functions, instead you are defining that double_add should use some function, but it only knows what it is when the code is actually run. (At runtime you are injecting the depedency on a specific function, instead of hardcoding it in the function itself),
Try for example the following
def add(a, b):
return a + b
def subtract(a, b):
return a - b
def double_add(x, a, b):
return x(x(a, b), x(a, b))
a = 4
b = 5
print(double_add(add, a, b))
print(double_add(subtract, a, b))
In other words, double_add has become a generic function that will execute whatever you give it twice and print the result

How to define two ways of iterate according input

What is the most pythonic form to define two ways of iterating. In example, I have this original code:
def f1(cat_gen):
for (a, b), c in cat_gen:
if some condition:
yield (a, b), c
but, depending on cat_gen I would need to iterate in this way:
def f1(cat_gen):
for a, b, c in cat_gen:
if some condition:
yield a, b, c
Is there a way to change conditionally just (a, b), c to a, b, c in the for statement
Pass a function that will evaluate the condition properly:
def f1(cat_gen, pred):
for item in cat_gen:
if pred(item):
yield item
f1(flat, lambda a, b, c: ...)
f1(nested, lambda ab, c: ...)
Or, flatten the nested tuples before passing the iterable to f1:
def f1(cat_gen):
for a, b, c in cat_gen:
if ...:
yield a, b, c
f1(map(lambda ab, c: (ab[0], ab[1], c), nested))
You could define it like this
def f1(cat_gen):
# Figure out how to iterate, store it in condition_to_iterate
for item in cat_gen:
if condition_to_iterate:
(a, b), c = item
else:
a, b, c = item
# Do whatever you need with a, b, c
If you want to keep it as 1 function, with two different return forms (maybe not the cleanest decision but depends on the implementation) you'd want to do something like:
def f1(cat_gen, yield_method = 0):
for a, b, c in cat_gen:
if some condition:
if yield_method:
yield a, b, c
else:
yield (a, b), c
and just have the user be aware of the modes of return by that second argument.

Python syntax clarification

Scrolling through the python 2.7 docs I came across this snippet
def fib(n): # write Fibonacci series up to n
a, b = 0, 1
while b < n:
print a,
a, b = b, a+b
But I don't understand the last line, and unsure of how I would google for it.
How should I read a, b = b, a+b, or, what does it mean ?
Python evaluates the right-hand side of assignments first. It evaluates
b, a+b
from left to right. It then assigns the values to the variables a and b respectively.
So a, b = b, a+b is equivalent to
c = b
d = a+b
a = c
b = d
except that it achieves the result without explicit temporary variables.
See the docs on Python's evaluation order.
There is a subtle point here worth examining with an example. Suppose a = 1, b = 2.
a, b = b, a+b
is equivalent to
a, b = 2, 1+2
a, b = 2, 3
So a gets assign to 2, b is assigned to 3.
Notice that this is not equivalent to
a = b
b = a + b
Since the first line would assign
a = 2
b = 2 + 2 = 4
Notice that done this (wrong) way, b ends up equal to 4, not 3. That's why it is important to know that Python evaluates the right-hand side of assignments first (before any assignments are made).
It is setting a to b, and b to a + b, without needing an intermediate variable. It could also be accomplished with:
temp = a
a = b
b = temp + b

Categories