I have a list with different values and a dictionary with different equations. I want to iterate with a for-loop through the list values and calculate a given equation. The example is below:
list = [1,2,3,4,5]
d ={"equation1": 2*x+3,
"equation2": 3+4*x}
for x in list:
y= d["equation1"]
print(y)
the output is as you can see below:
5
5
5
5
5
When I put the dictionary into the forloop like the sample below, the right values are calculated:
list = [1,2,3,4,5]
for x in list:
d ={"equation1": 2*x+3,
"equation2": 3+4*x}
y= d["equation1"]
print(y)
Result:
5
7
9
11
13
How can I realise the right results with the dictionary outside the for-loop?
In your first example:
list = [1,2,3,4,5]
d ={"equation1": 2*x+3,
"equation2": 3+4*x}
This can only work if you somehow defined x previously, so you probably didn't share all of your relevant code.
Something like 2*x+3 gets evaluated immediately, so if x == 1, you'd get the result 5, as you suggest you do.
You could create a dict with lambda expressions:
d ={"equation1": lambda x: 2*x+3,
"equation2": lambda x: 3+4*x}
But I wouldn't recommend it as good code. If you do though, this works:
list = [1,2,3,4,5]
d ={"equation1": lambda x: 2*x+3,
"equation2": lambda x: 3+4*x}
for x in list:
y = d["equation1"](x)
print(y)
Note how on the line y = d["equation1"](x) x gets passed to the function accessed with d["equation1"] - a lambda expression is a simple function and the ones defined in the dict expect a single parameter.
A more appropriate solution would be:
def equation1(x):
return 2*x+3
def equation2(x):
return 3+4*x
xs = [1,2,3,4,5]
for x in xs:
y = equation1(x)
print(y)
And then if you have reason to refer to your functions with a dict from somewhere:
xs = [1,2,3,4,5]
funs = {
'equation1': equation1,
'equation2': equation2
}
for x in xs:
y = funs['equation1'](x)
print(y)
The problem you have is that the expressions 2*x+3 and 3+4*x aren't something you can store in a dictionary. Rather, they get computed immediately, with whatever value of x is available when the dictionary is created. They do not use the value of x when the value gets looked up later.
If you want to store some code, like those expressions, to be run later, the natural way to do it in Python is using a function. The lambda keyword even lets you create functions that only evaluate a single expression, which is exactly what you want.
Try:
d = {"equation1": lambda: 2*x+3,
"equation2": lambda: 3+4*x}
for x in list:
d ={"equation1": 2*x+3,
"equation2": 3+4*x}
y = d["equation1"]() # note, here we're calling the lambda function
print(y)
A few other notes:
It might make sense to use a different container than a dictionary for d, since your equation names don't appear to have a great deal of meaning. If you just want to index them by integer (e.g. 1 instead of "equation1"), a list is a much more natural choice.
Also, your equations currently pick the x value freely out of the enclosing namespace. It might be cleaner to pass in the x value as an argument to the lambda function. Grismar's very nice answer covers how to do this for x, so I won't give any redundant example code. Passing the values the equation needs as arguments does require a stricter "contract" to be established between the calling code and the equations though, as if one equation expects there to be a y or z variable defined, you need to know to pass it in as an argument.
I run into a problem when unpacking a tuple. I want the first value to be appended to a list and a second assigned to a variable. For example:
list = []
tuple = (1, 2)
list.append, variable = tuple
But this raises an exception since I am assigning to a bultin and not actually calling in. Is that possible in Python? Or even a simpler operation such as:
a, b = 5, 4
tuple = (1, 2)
a+, b = tuple
to yield a = 6, b = 2.
There's no brief syntax to allow this. However, here's a class that creates a wrapper around a list, so that assigning to an append attribute really calls the underlying list's append method. This could be useful if you have a lot of values to append to the list.
class Appender:
def __init__(self, lst):
self.lst = lst
# The rare write-only property
append = property(None, lambda self, v: self.lst.append(v))
values = []
value_appender = Appender(values)
value_appender.append, b = (1,2)
assert values == [1]
Perhaps simpler, a subclass of list with a similar property:
class Appendable(list):
take = property(None, lambda self, v: self.append(v))
values = Appendable()
values.take, b = (1, 2)
assert values == [1]
append is a method on the builtin list type. Python allows tuple unpacking into variables in one line as a convenience, but it won't decide to call the append method with part of your tuple as an argument. Just write your code on multiple lines, that will help make it easier to read too.
my_list = []
my_tuple = (1, 2)
a, b = my_tuple
my_list.append(a)
Technically yes you could do it in a single line, but I wouldn't.
l = []
a = (1,2)
l[:0], b = [[x] if c == 0 else x for c,x in enumerate(a)]
>>> l
[1]
>>> b
2
You can use the map function on the append method for the list.
>>> a = (6,7)
>>> b = [1,2,3,4,5]
>>> list(map(b.append, a))
[None, None]
>>> b
[1, 2, 3, 4, 5, 6, 7]
I am not really sure what the list() does in this statement but it seems to work.
I apologize if this question has been asked before, as it seems to be very basic. Unfortunately, when I searched for my question, I could only find other questions asking how to iterate over a list of lists, and none of these questions touched on the specific behavior I am asking about.
I am aware that in python, when you equate two lists, you are not actually copying that list in memory, just creating a new alais pointing to that list in memory. so something like
listA = [1,2,3]
listB = listA
listB[0] = 5
print(listA) #prints [5,2,3]
makes perfect sense to me.
I also know that you can modify mutable types (like lists) in a for loop, while for other types (like integers), you cannot, and must modify the original list. for example
listA = [1,2,3]
listB = [4,5,6]
for Int in listA:
Int +=1
print(listA) #doesn't work, prints [1,2,3]
for List in [listA,listB]:
List[2] = 100
print(listA) #works, prints [1,2,100]
my problem appeared when I tried to combine these two principles. Here is an example of what I tried to do:
x = [1.2345,0.543895,0.0]
y = [2,3,4]
z = [65.34,3.248578493,1.11111]
for coord in [x,y,z]:
rounded_coord = [round(item,3) for item in coord]
coord = rounded_coord
print(x,y,z) #prints unmodified numbers
In this example, 'coord' is a list, and therefore I should be able to modify it, just like listA in my previous examples, but I can't. I have to use enumerate:
x = [1.2345,0.543895,0.0]
y = [2,3,4]
z = [65.34,3.248578493,1.11111]
coordlist = [x,y,z]
for idx,coord in enumerate(coordlist):
coordlist[idx] = [round(item,3) for item in coord]
print(coordlist)
Why doesn't my original attempt work?
'coord' is a list, and therefore I should be able to modify it...
Almost, but not quite. coord is a variable that stores a reference to the each of the original lists in turn per iteration.
rounded_coord is also a variable that stores a reference to a new list.
Now, doing coord = rounded_coord will make the variable coord point to the same reference as rounded_coord. Meaning, the original contents of coords will remain unchanged while the reference that coord points to changes.
Example:
>>> x = [1, 2, 3, 4, 5]
>>> for l in [x]:
... print(id(l))
... new_l = [1]
... l = new_l
... print(id(l))
...
4309421160
4309421592
By the way, id prints a 10 digit number representing the reference a variable points to. You can see that at the start vs at the end, the reference stored in l changes.
In your example:
x = [1.2345,0.543895,0.0]
y = [2,3,4]
z = [65.34,3.248578493,1.11111]
for coord in [x,y,z]:
rounded_coord = [round(item,3) for item in coord]
coord = rounded_coord
print(x,y,z) #prints unmodified numbers
coord is not a list - it's a pointer to a list in your ad-hoc created [x, y, z] list. Since this is a relatively simple example, here is how this unpacks without a loop:
coord = x
rounded_coord = [round(item,3) for item in coord]
coord = rounded_coord
coord = y
rounded_coord = [round(item,3) for item in coord]
coord = rounded_coord
coord = z
rounded_coord = [round(item,3) for item in coord]
coord = rounded_coord
See a problem with it?
If you really wanted to change the element itself, you'd either have to reference it by index in its container list on replacement (as you've noticed that it works with enumerate) or to replace the value in place:
for coord in [x,y,z]:
coord[:] = [round(item,3) for item in coord] # assuming not overriden __setslice__()
Every time you use a loop of the form:
for item in list:
#do something
The item becomes a reference to each element in the list as it loops through. Using modifiers such as:
item[2] = 5
will work because you are modifying the reference directly.
However, when you try to set the reference equal to another variable:
item = some_other_thing
you are not actually modifying the item, you are modifying the reference to the item, and have therefor never changed the original item, just lost your reference to it.
In all your cases, the loop variable is just an alias for each element in the thing being looped over.
The reason List in your second example works like you want it to, but coord in your third example doesn't, is that in the second example, you're changing an element of the list pointed to by List, and therefore actually updating the underlying data.
In the third example, you're trying to directly change coord, and thus are just updating the alias to point to a different list, instead of using the alias to access the underlying array and changing it. As a result, coord now just points to a different list. Consider this modified version of your second example:
for List in [listA,listB]:
List = [7,8,9]
print(listA) # prints [1,2,3]; underlying data not changed
print(List) # prints [7,8,9]; List now points to the new array
To modify elements of the list, you need to provide the index and then modify. In other words, this will not modify anything in the list:
arr=[1,2,3]
for x in arr:
x+=1 #not modifying the values in the list
print(x) #this will print 2,3,4
print(arr) #prints [1,2,3]
So even though you incremented the values but you did not replace the values in the list
What you need to do is, use the index to change the values
arr=[1,2,3]
for x in range(len(arr)): #len(arr) evaluates to 3 and range(3) makes an array of numbers from 0 up to but not including 3
arr[x]=arr[x]+1
print[arr] #Now prints [2,3,4]
The issue is that assignment never mutates. It merely creates a new reference, or reassigns a reference (which is what you are doing). You have to use a mutator method, so for example:
In [1]: x = [1.2345,0.543895,0.0]
...: y = [2,3,4]
...: z = [65.34,3.248578493,1.11111]
...: for coord in [x,y,z]:
...: coord[:] = [round(item,3) for item in coord]
...: print(x,y,z)
...:
[1.234, 0.544, 0.0] [2, 3, 4] [65.34, 3.249, 1.111]
So, even though coord[:] = [round(item,r) for item in cord] looks like a regular assignment, it is not. It is syntactic sugar for calling a mutator method, __setitem__ to be precise:
In [2]: coord
Out[2]: [65.34, 3.249, 1.111]
In [3]: coord.__setitem__(slice(None,None), ['foo','bar','baz'])
In [4]: coord
Out[4]: ['foo', 'bar', 'baz']
Similarly, List[0] = 100 is not assignment, it is syntactic sugar for:
In [6]: List
Out[6]: [1, 2, 3]
In [7]: List[0] = 99
In [8]: List
Out[8]: [99, 2, 3]
In [9]: List.__setitem__(0, 'foo')
In [10]: List
Out[10]: ['foo', 2, 3]
You can't modify immutable types because, by definition, they have no mutator methods, even though there are operations that look like mutator methods:
x = 10
x += 10 # looks like a mutator method but actually assigns a new object to x
Note, a str is similar to a list. You can slice it, but the corresponding mutator method doesn't exist:
In [17]: s = 'abcdefg'
In [18]: s[:3]
Out[18]: 'abc'
In [19]: s.__getitem__(slice(0,3))
Out[19]: 'abc'
In [20]: s.__setitem__
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-20-44ee26769121> in <module>()
----> 1 s.__setitem__
AttributeError: 'str' object has no attribute '__setitem__'
Your first example did not work because coord is only a reference to to the lists x, y, and z. When you reassign coord, you are overwriting the original reference and setting coord to reference a new list object.
In other words coord is not a pointer to each list. Rather, it is only a reference. And when you reassign coord, you lose the original reference.
You can remedy this problem by mutating the list object coord is referencing:
>>> x = [1.2345, 0.543895, 0.0]
>>> y = [2, 3, 4]
>>> z = [65.34, 3.248578493, 1.11111]
>>>
>>> for coord in [x, y, z]:
coord[:] = [round(item,3) for item in coord]
>>> x
[1.234, 0.544, 0.0]
>>> y
[2, 3, 4]
>>> z
[65.34, 3.249, 1.111]
>>>
I am trying to write a function that squares each value in a list, returning a new list with individual values squared. I pass a list to it and it changes the original list. I'd like it to not make any changes to the original list, so that I can use it in other functions.
def squareset(c):
d=c
count = len(c)
print(c)
for i in range(0,(count),1):
d[i]=d[i]**2
return d
test = [1,2,3]
print(squareset(test))
print(test)
I don't have this problem with functions operating on simple variables of type int or float.
I added the d=c line trying to prevent the change to the list test, but it makes no difference. print(test) is producing the result [1,4,9] instead of [1,2,3]. Why is this happening and how can I fix this?
Doing d=c simply makes the parameter d point to the same object that c is pointing to. Hence, every change made to d is made to the same object that c points to.
If you want to avoid changing c you'll have to either send a copy of the object, or make a copy of the object inside the function and use this copy.
For example, do:
d = [i for i in c]
or:
d = c[:]
instead of:
d = c
Assigning the list to another variable doesn't copy the list. To copy, just
def squareset(c):
d=c[:]
...
While the other answers provided are correct I would suggest using list comprehension to square your list.
In [4]:
test = [1,2,3]
results = [elm**2 for elm in test]
print test
print results
[1, 2, 3]
[1, 4, 9]
If you wanted a function:
def squareList(lst):
return [elm**2 for elm in lst]
Try this:
def square(var):
return [x*x for x in var]
x = [1,2,3]
z = square(x)
I know that Python has built-in list functions but I'm curious as to how one would write a function to sum a list and a function to reverse a list. I was able to figure out how to write some other list functions (sort, count, index, etc.) but not these, and I imagine some other languages don't have these built-in functions.
Could someone show me some Python code for those 2 functions, not using any other built-in functions?
For summing a list, you can do:
sum([1, 2, 3, 4])
And for reversing a list, this will return a new, reversed list by using Python's slices:
[1, 2, 3, 4][::-1]
Now if you don't want to use built-in functions:
def sum(lst):
s = 0
for e in lst:
s += e
return s
def reverse(lst):
l = []
for e in lst:
l = [e] + l
return l
Sum a list
Straight from the Python manual:
>>> def sum(seq):
... def add(x,y): return x+y
... return reduce(add, seq, 0)
>>> sum(range(1, 11))
55
>>> sum([])
0
This could be done as a one-liner (...ish) using lambda (Python's anonymous function syntax):
def sum(seq):
return reduce(lambda x, y: x + y, seq, 0)
Don't want to use reduce?
def sum(seq):
total = 0
for s in seq:
total += s
return total