Python list of objects vs list of primitives - python

Following is the MWE:
class Foo():
def __init__(self, data):
self.data = data
def __repr__(self):
return str(self.data)
object_list = [Foo(10), Foo(80), Foo(50), Foo(60), Foo(20)]
int_list = [10, 80, 50, 60, 20]
print("Object list before modification: {}".format(object_list))
print("Integer list before modification: {}".format(int_list))
for i in object_list:
i.data += 5
for j in int_list:
j += 5
print("")
print("Object list after modification: {}".format(object_list))
print("Integer list after modification: {}".format(int_list))
The output is as follows:
Object list before modification: [10, 80, 50, 60, 20]
Integer list before modification: [10, 80, 50, 60, 20]
Object list after modification: [15, 85, 55, 65, 25]
Integer list after modification: [10, 80, 50, 60, 20]
Why are the results different? In my opinion, the integer result seems more natural as we are not exactly performing the operation on the list (like for idx in range(len(object_list)): object_list[idx].data += 5). We are rather applying it on the elements after we take them out.

This is the strangeness of augmented assignment. "+=" takes three steps: 1) get the value from the left hand side, 2) add the value on the right hand side 3) store the result back in the variable on the left hand side.
In your first case, the left hand side is i.data and i.data is reassigned to the result. Since the object in i is in the original list, its reassigned attribute "data" shows the change.
In your second case, the left hand side is j. It us updated properly, but since j isn't saved anywhere, its value is lost.

Related

Trying to swap the first and last elements of a list in Python, but it works only for a predefined list. Not for a list of randomly generated numbers

I was trying to create a python program which swaps the first and last elements of a list. I passed a pre-created list into the algorithm and it worked perfectly. Here's my code:
def swapFirstAndLast(list_to_be_swapped):
size = len(list_to_be_swapped)
list_to_be_swapped[0],list_to_be_swapped[size-1] = list_to_be_swapped[size-1],list_to_be_swapped[0]
return list_to_be_swapped
l = [12,33,42,76,46,97]
swapFirstAndLast(l)
print(l)
Output:
[97, 33, 42, 76, 46, 12]
Then I tried to create functions; one function to create a list of randomly generated numbers, and the second function to perform the swapping operation. Although everything makes sense to me, it is not performing the swapping operation now. This is the code I came up with:
import random
def generateList(size):
list1 = []
for i in range(size):
list1.append(random.randint(0,99))
return list1
def swapFirstAndLast(list_to_be_swapped):
size = len(list_to_be_swapped)
list_to_be_swapped[0],list_to_be_swapped[size-1] = list_to_be_swapped[size-1],list_to_be_swapped[0]
return list_to_be_swapped
l = generateList(5)
l1 = swapFirstAndLast(l)
print(l,l1)
Output:
[49, 78, 63, 82, 72] [49, 78, 63, 82, 72]
As you can see, it does not perform the swapping operation now. I am not able to understand where I am going wrong.
You are swapping the first and the last element of the initial list (i.e., l) too! Please look at this slightly modified example:
import random
def generateList(size):
list1 = []
for i in range(size):
list1.append(random.randint(0,99))
return list1
def swapFirstAndLast(list_to_be_swapped):
size = len(list_to_be_swapped)
list_to_be_swapped[0],list_to_be_swapped[size-1] = list_to_be_swapped[size-1],list_to_be_swapped[0]
return list_to_be_swapped
l = generateList(5)
print(l)
l1 = swapFirstAndLast(l)
print(l, l1)
Output:
[54, 14, 3, 38, 87]
[87, 14, 3, 38, 54] [87, 14, 3, 38, 54]
As you can see, the list l has been changed.
The thing here is that you are not creating a new list, but you're modifying the existing one. It doesn't matter if it has a different name within the function.
If you want to retain the original list l, and also return a separate swapped list l1, you have to create a new list! Here is how you can do it:
import random
def generateList(size):
return [random.randint(0, 99) for _ in range(size)]
def swapFirstAndLast(list_to_be_swapped):
new_list = list_to_be_swapped.copy()
new_list[0], new_list[-1] = new_list[-1], new_list[0]
return new_list
l = generateList(5)
print(l)
l1 = swapFirstAndLast(l)
print(l, l1)
Output:
[38, 59, 86, 26, 19]
[38, 59, 86, 26, 19] [19, 59, 86, 26, 38]
your program works ! your function just modifies the list directly, you can see it better if you do this :
l = generateList(5)
print(l)
l1 = swapFirstAndLast(l)
print(l1)
It turns out that you have already swapped the list (i.e. l) it's just when your print (l,l1) that it looks like you haven't swapped it because it's printing the swapped version of (l). put the print(l) line above ( l1 = swapFirstAndLast(l) ) to see it!
the swapping can be done by using index:
def swapFirstAndLast(lst):
lst[0], lst[-1] = lst[-1], lst[0]
return lst
lst = [12,33,42,76,46,97]
print(swapFirstAndLast(lst))
result is: [97, 33, 42, 76, 46, 12]

Split a list in for loop based on indices of list

This is a simplified version of some code im working on, a kind of toy model so i can focus only on the bit thats troubling me. This is why i have a function defined for finding the minimum, rather than simply using the numpy command. In my proper code, a fucntion is required, since its not as simple as using a pre-existing numpy function.
What i want to do is split my list into 2 list at the minimum point - so given what i have here, i would ideally get [15, 62, 49, 49, 4] and [100, 71, 16, 70, 62] . HOwever, i dont get this. I get all the points stored in node1, and nothing in node2. i really cant figure out why - as you can see, ive tried 2 ways of doing the for loop. i know completely the idea of the code, that the loop should run over the indices of the list and then store the values of these indices in the new lists, node1 and node2. what am i doing wrong?
#generate random list of numbers
import random
randomlist = [] #==profile_coarse
for i in range(0,10):
n = random.randint(1,100)
randomlist.append(n)
print(randomlist)
#define a function to find minimum
def get_min(list):
mini=np.min(randomlist)
return mini
node1=[]
node2=[]
for i in enumerate(randomlist):
if i<=get_min(randomlist):
node1.append(randomlist[i])
else:
node1.append(randomlist[i])
#OR
for i in range(0,len(randomlist)):
if i<get_min(randomlist):
node1.append(randomlist[i])
else:
node1.append(randomlist[i])
print(node1,node2)
which yields
[15, 62, 49, 49, 4, 100, 71, 16, 70, 62] []
you could use the built-in functions enumerate and min:
randomlist = [15, 62, 49, 49, 4, 100, 71, 16, 70, 62]
#define a function to find minimum
def get_min(l):
mini= min(enumerate(l), key=lambda x: x[1])
return mini
index_min, min_val = get_min(randomlist)
node1 = randomlist[:index_min + 1]
node2 = randomlist[index_min + 1:]
print(node1, node2)
print(node1)
print(node2)
output:
[15, 62, 49, 49, 4] [100, 71, 16, 70, 62]
enumerate returns a tuple of a a counter (by default starting at 0) and the one value from the iterable you are passing.
In your code i would be (0, 15), (1, 62)... etc.
My guess is that you simply want to do for i in randomlist
Keep in mind using a min function will in any case put all the values in one node and not in 2 as you want it to do.

How to remove items from smaller lists that are in a larger list?

Hi I have one big list which consist of 10 smaller list. In each smaller list are 10 random numbers.
Depending on the variable, either even or not even variables must be visible.The example below I made only for 2 smaller lists to make it easier to see the output.
This is my code.
def generate_random_number_list_10(len_of_elements):
my_list=[random.randint(1,100) for x in range (len_of_elements)]
return my_list
def generate_list_consist_of_10_smaller_list(amount_of_list,len_of_elements,variable):
big_list=[generate_random_number_list_10(len_of_elements) for x in range (amount_of_list)]
print (big_list)
if variable%2 == 0:
for num in big_list :
for x in num:
filtered_list = list(filter(lambda x: x%2==0, num))
print (filtered_list)
generate_list_consist_of_10_smaller_list(2,8,8)
This is my output:
[[1, 47, 25, 84, 48, 8, 91, 89], [99, 40, 54, 50, 52, 83, 86, 61]] - for example big list cosist of 2 smaller list
[40, 54, 50, 52, 86] - my solution is correct only for last smaller list
[[ 84, 48, 8], [ 40, 54, 50, 52, 86]] -this is correct solution
For a larger number of smaller lists, my solution filters even numbers only for the last smaller list.
How to apply filtering to all smaller lists, regardless of how many there are?
filtered_list needs to be a list of lists.
Also, you don't need the for x loop, since filter() does that looping automatically.
def generate_list_consist_of_10_smaller_list(amount_of_list,len_of_elements,variable):
big_list=[generate_random_number_list_10(len_of_elements) for x in range (amount_of_list)]
print (big_list)
if variable%2 == 0:
filtered_list = [list(filter(lambda x: x%2==0, num)) for num in big_list]
print (filtered_list)

add neigbouring elements in a list

How can I write a function which takes a list of integers and creates a new list with the same number of elements as the original list such that each integer in the new list is the sum of its neighbours and itself in the original list. For example, if a_list = [10, 20, 30, 40, 50], the new_list = [30, 60, 90, 120, 90].
I tried a list comprehension;
def sum_neighbours(a_list):
b_list = a_list
li = [x+y for x in a_list for y in b_list]
return li
print(sum_neighbours([10, 20, 30, 40, 50]))
Which gave me;
[20, 30, 40, 50, 60, 30, 40, 50, 60, 70, 40, 50,
60, 70, 80, 50, 60, 70, 80, 90, 60, 70, 80, 90, 100]
I then tried a for loop;
def sum_neighbours(a_list):
element1 = a_list[0]
new_list = []
for element in a_list:
element1 += element
new_list += element1
return new_list
print(sum_neighbours([10,20,30,40,50]))
Which threw a TypeError: 'int' object is not iterable
But in both cases I don't think my ideas/attempts are on the right track... I would really appreciate any help, hints, tips that more experienced people may have.
You can start with a solution that iterates through the indexes of the list, like so:
def sum_neighbours1(a_list):
result = []
for i in range(len(a_list)):
if i == 0:
result.append(a_list[i] + a_list[i+1])
elif i == len(a_list)-1:
result.append(a_list[i-1] + a_list[i])
else:
result.append(a_list[i-1] + a_list[i] + a_list[i+1])
return result
print(sum_neighbours1([10,20,30,40,50]))
Or, you can build three distinct lists: the original list, the list of left-hand neighbors, and the list of right-hand neighbors. We have to put 0 at the front or rear of the auxiliary lists. Each of the lists must be at least as long as the original list.
Zip the three lists together, and add them:
def sum_neighbours2(a_list):
priors = [0] + a_list
subsequents = a_list[1:] + [0]
return map(sum, zip(priors, a_list, subsequents))
Note that I used the functional notation map(sum, data), but I could have used a list conprehension just as easily:
def sum_neighbours3(a_list):
priors = [0] + a_list
subsequents = a_list[1:] + [0]
return [p+a+s for p,a,s in zip(priors, a_list, subsequents)]
And, if I were eager to make the result a single expression (for example, if it had to fit in a lambda expression), here that is:
def sum_neighbours4(a_list):
return [
left + a + right
for left, a, right in zip(
[0] + a_list,
a_list,
a_list[1:] + [0])
]
Here is my one line solution with zip :
def sum_neighbours(a) :
return [sum(x) for x in [(a[0],a[1])]+zip(a,a[1:],a[2:])+[(a[-2],a[-1])]]
print sum_neighbours([10, 20, 30, 40, 50]) # display [30, 60, 90, 120, 90]
with :
a, the original list
zip(a,a[1:],a[2:]), 3-tuples generation
[(a[0],a[1])] and [(a[-2],a[-1])], 2-tuples for particular cases (first and last numbers of list)
You'll probably want a for i in range loop, and indexing directly into the array so you can look at the element that is at i+1 and i-1 indexes (making sure to check that your +/- 1 isn't going outside the bounds of the array).

sum of surrounding elements in a list

I'm writing a code which calculates the sum of the numbers beside it.
For example, list1 = [10, 20, 30, 40, 50], the new list = [30 (10+20), 60 (10+20+30), 90 (20+30+40), 120 (30+40+50), 90 (40+50)]. => final list = [30, 60, 90, 120, 90].
At the moment my idea was of using a for loop but it was totally off.
You can do it by creating triplets using zip:
# pad for first and last triplet
lst = [0] + original + [0]
# summarize triplets
sums = [sum(triplet) for triplet in zip(lst, lst[1:], lst[2:])]
Example:
>>> original = [10, 20, 30, 40, 50]
>>> lst = [0] + original + [0]
>>> sums = [sum(triplet) for triplet in zip(lst, lst[1:], lst[2:])]
>>> sums
[30, 60, 90, 120, 90]
>>>
Check out this guy's flatten function What is the fastest way to flatten arbitrarily nested lists in Python?
Take the result of the flattened lists of lists and sum the collection normally with a for loop, or a library that provides a count utility for collections.

Categories