On the Literate Programs site, I was looking at the Python code for the GCD algorithm.
def gcd(a,b):
""" the euclidean algorithm """
while a:
a, b = b%a, a
return b
What is going on in the body? Expression evaluation? Is it a compressed form of another structure?
There are two things going on here:
a, b = b%a, a
First, a tuple is created with the contents (b%a, a). Then the contents of that tuple are unpacked and assigned to the names a and b.
Looks like shorthand for:
while a > 0:
temp = a
a = b%a
b = temp
return b
a is receiving the result of b%a while b is receiving the value of a
Works the same as:
while a > 0:
tmp = a
a = b%a
b = tmp
return b
See this post for more information on switching variables: Is there a standardized method to swap two variables in Python?
Related
I'm aware yield generates a value on the fly, by my understanding this means it doesn't keep the value in the memory, and therefore the current value shouldn't be able to interact with the last values.
But I just want to be sure that's the case, could someone confirm if it's possible or not?
I'm going to use 5 as the value in number.
Example without generator:
def factorial(number):
result = number
if number <= 1:
return 1
else:
for x in reversed(range(1, number)): # (4,1) reversed
result *= x # 5*4*3*2*1
return result # returns 120
Is it possible to do the same thing by using the yield function? how?
Thank you
Generators can be stateful:
def fibs():
a, b = 1, 1
while True:
yield b
a, b = b, a + b
g = fibs()
for i in range(10):
print next(g)
Here the state is in the local variables. They are kept alive while the iterator generated by the generator is alive.
EDIT. I'm blind it was a factorial
def factorials():
i = 1
a = 1
while True:
yield a
i+=1
a*=i
or if you need a function not a stream of them then here's a one liner
print reduce(lambda a, b: a*b, (range(1, 10+1)))
Is it possible to loop over a list that I define within the for statement and modify the original elements of that list or do I need a name / handle for that list to access its values by an index? I am aware of the alternatives that exist for lists with a variable name.
The following code does not alter a, b and c.
# in each iteration, this assigns item to the value behind a, b, or c
for item in [a, b, c]:
# this reassigns item to a new value, but there is no reference to a, b, or c
# (which I would like to have).
item = some_function(item)
Simplified example (realistic example at the end):
a = 1
b = 3
c = 7
for item in [a, b, c]:
item = item + 1
print("a: {}; b: {}; c: {}".format(a,b,c))
Output:
a: 1; b: 3; c: 7
Desired output:
a: 2; b: 4; c: 8
In my actual use case, a, b and c are numpy arrays and I want to clip their values like this:
for gradient in [a, b, c]:
gradient = np.clip(gradient, -max_value, max_value)
Conclusion / Answer
Thanks to the valuable input from bruno desthuilliers, I can now answer this question myself and add some explanations:
The assignment operator only reassigns the loop variable to some new value and that does not alter the elements in the original list. There is a great article on how names / variables and assignments work in Python and I can strongly recommend it: Facts and myths about Python names and values.
In order to modify the original items in the list (either have them point to some new data or modify their data), one must either rely on methods that operate on the data in-place or hope for specific methods provided by the type of entry in the list (see this helpful example from cricket_007).
In my specific use case, I found the final solution in an optional out argument of the numpy.clip method:
for gradient in [a, b, c]:
np.clip(gradient, -max_value, max_value, out=gradient)
However, the provided answers below, based on map() or list comprehension, are more universal.
Thank you very much for your help.
You cannot do it that way because the variable item is the one being updated and rebound within the loop body, and again after iteration. The values within the list itself aren't updated.
You could do this with a class so you have some object to refer back to after the loop
class Var():
def __init__(self, x):
self.x = x
def incr(self):
self.x += 1
def __str__(self):
return str(self.x)
a = Var(1)
b = Var(3)
c = Var(7)
for item in [a, b, c]:
item.incr()
print("a: {}; b: {}; c: {}".format(a,b,c))
Alternatively, you're looking for something like this
a, b, c = map(lambda x: x+1, [a, b, c])
This:
a, b, c = [np.clip(gradient, -max_value, max_value) for gradient in (a, b, c)]
would mostly do what you want - but beware that any other name referring to those arrays will still refer to the original (unclipped) values since np.clip() returns a new array.
For an in-depth explanation of why and hows, I strongly suggest you take time to read and fully understand Ned Batcheler's excellent article on Python's names, values and bindings.
I am new to Python and have difficulties to understand how to use loop.
could anyone help me how to write below code in a loop/list, which will be much shorter?
def a1(self):
if aorb1 == 1:
return texta1
return textb1
def a2(self):
if aorb2 == 1:
return texta2
return textb2
def a3(self):
if aorb3 == 1:
return texta3
return textb3
Thanks a lot.
I can take a stab at what direction you are going and provide some level of guidance, but I suspect that I won't go the correct direction without more information.
First off, you don't need self as a parameter. That only applies to objects.
Next, you do need to provide the variables used within the function as parameters. It looks like you tried to use a and b without declaring them.
def a1(a, b)
if a == 1:
return texta
elif b == 1:
return textb
Be careful that you don't miss any cases. What if a = 0 and b = 0? Then this function would return None.
Finally, I'm not sure exactly what you are trying to do with the loop, but perhaps something like this?
# assign a and b values somewhere
a = 1
b = 0
# save the functions in a list
my_functions = [a1, a2, a3]
# execute each function with the parameters `a` and `b`
for f in my_functions:
result = f(a, b)
x.append(result)
This will result in a list containing the results of your function executions with parameters a and b. Would like to help more, but we need more information. Perhaps the above will stimulate that.
You could generate lambdas where you pass a and b, as follows:
my_funcs = []
for i in range(1,4):
func = lambda a, b: textas[i] if a == 1 or b == 1 else textbs[i]
my_funcs.append(func)
You can call such a function like this:
my_funcs[0](0,1) # this does the same as your function a1
I don't know what you want texta1 or textb1 to be. This could be stored in a dictionary, like I do in the example above.
My code is as follows...
def addition(a, b):
c = a + b
return c
And I then want to be able to use C later on in the program as a variable. For example...
d = c * 3
However, I get a NameError that 'C' is not defined... But I have returned c, so why can I not use it later on in the code?! So confused. Thanks!
(This is obviously a simpler version of what I want to do but thought I'd keep it simple so I can understand the basics of why I cannot call on this variable outside my function even though I am returning the variable. Thanks)
You have returned the value of c but not the whole variable i.e. the name c exists only within the scope it is instantiated.
So, if you want to use the value returned, you should re-assign it to a new name. You can do it by re-assigning it to c again, but it could be any name you wanted.
def addition(a, b):
c = a + b
return c
new_var = addition(1,2) #new_var gets the value 3
c = addition(2,3) #c gets the value 5
Take a look at this nice explanation about variables and scopes (link)
You usually define a function to use it later in your code. For that case, use another global variable c:
def addition(a, b):
c = a + b
return c
c = addition(1, 2)
d = c * 3 # d == 9
Functions allow this usage of repeated code, or procedure distinction, so that you can later write in your code
m = addition(4, 5)
and it will store the required result of the functionality into m.
If you want to define c in the function and use it later, you can use global variables.
c = 0
def addition(a, b):
global c
c = a + b
return c
It's not considered good to use globals, though. You could also call the function in the variable assignment.
d = addition(a, b) * 3
For this, you need to put real numbers in the place of a and b. I recommend you use the second option.
When I am using numpy, I can sum two 1D array simply by writing:
C = A + B
If I have a list of objects, and all the objects in the list are of the same class and all have three arguments (r, v and a). If I want to compute lets say r = r + v +0.5*a for all objects in the list:
How do I write this in one line like I would do in numpy knowing that all my object are in one list L ?
Something like this I guess ?:
L.r += L.v + 0.5*L.a
It's a bit unclear what the output you are looking for. Are you looking for a list of numbers:
[x.r + x.v + .5*x.a for x in L]
The summation of those?
sum([x.r + x.v + .5*x.a for x in L])
Or changing the objects themselves?
runningSum=0
def mod(obj):
# How complicated this function is depends on what your trying to accomplish.
obj.r = runningSum + obj.r + obj.v + .5*obj.a
runningSum = obj.r
return obj
map(mod,L)
If you can't survive with two lines:
[setattr(item,'r',item.r + item.v + 0.5 * item.a) for item in L]
While not a formal answer, I would like to pick up your curiosity and make you imagine a world of possibilities arising from your question. If you don't want to use numpy for some reason, and you just wonder how to make the sum of all elements within a list in just one line, you may want to redefine the operation between lists. This can be achieved by defining a very simple class like the following one:
class array():
def __init__(self, mylist):
self.list = mylist
return
def __add__(self, other):
for i in xrange(len(self.list)):
self.list[i] += other.list[i]
return array(self.list)
def __repr__(self):
return "%s" % (self.list)
You can see that the class is instantiated using a python list, and then I define the __add__ method, which essentially indicate how the array objects interact under the + symbol. Of course, there is plenty of room here to improve it and check for exceptions, but this is only to highlight the fact that you can define whatever you want. You can see that the addition of two array objects returns a new array object, which allow us to keep adding.
Just for you to see how does it work, below I write a very simple example:
A = array([1,2,3,4,5])
B = array([3,2,4,5,6])
C = A+B
Then if you type C in the prompt, you will see
[4, 4, 7, 9, 11]
which again is an array class.
If you can survive with two lines try this:
for item in L:
item.r += item.v + 0.5 * item.a