Object ID assignment in Python (for lists) - python-3.x

I recently discovered this interesting behavior of Python when it doesn't generate unique object IDs after instantiating a couple of new list objects.
Let me demonstrate:
print('id([1, 2, 3])= ', id([1, 2, 3]))
a = [1, 2, 3]
print('a= ', a)
print('id(a)= ', id(a))
print('id([1, 2, 3])= ', id([1, 2, 3]))
print('id(a)= ', id(a))
The output in terminal:
id([1, 2, 3])= 140117092252416
a= [1, 2, 3]
id(a)= 140117092252416
id([1, 2, 3])= 140117090393920
id(a)= 140117092252416
Despite calling [1,2,3] multiple times, there are two unique object IDs available.I got confused. Shouldn't they be the same in my mind?

Why should these object have the same IDs? They are different instances, that just happen to have the same content. String and numeric literals have the same ID, but that's an optimization that's only allowed since they're immutable.
If the two lists would have the same ID, it would mean they'd be the same instance of a list. That would also mean that you could change a by doing this:
a = [1, 2, 3]
[1, 2, 3].append(4)

Related

How can I get multiple output variables into a list?

I'm wondering if there's a way of getting multiple outputs from a function into a list. I'm not interested in creating a list inside of a function for reasons I'm not going to waste your time going into.
I know how many output variables I am expecting, but only through using the annotations["return"] expression (or whatever you call that, sorry for the noobish terminology) and this changes from case to case, which is why I need this to be dynamic.
I know I can use lists as multiple variables using function(*myList), but I'm interested in if there's a way of doing the equivalent when receiving return values from a function.
Cheers!
Pseudocode:
function():
x = 1
y = 2
return x, y
variables = function()
print(variables[0], " and ", variables[1]
result should be = "1 and 2"
yes, with the unpacking assignments expression ex a,b,c= myfunction(...), you can put * in one of those to make it take a variable number of arguments
>>> a,b,c=range(3) #if you know that the thing contains exactly 3 elements you can do this
>>> a,b,c
(0, 1, 2)
>>> a,b,*c=range(10) #for when you know that there at least 2 or more the first 2 will be in a and b, and whatever else in c which will be a list
>>> a,b,c
(0, 1, [2, 3, 4, 5, 6, 7, 8, 9])
>>> a,*b,c=range(10)
>>> a,b,c
(0, [1, 2, 3, 4, 5, 6, 7, 8], 9)
>>> *a,b,c=range(10)
>>> a,b,c
([0, 1, 2, 3, 4, 5, 6, 7], 8, 9)
>>>
additionally you can return from a function whatever you want, a list, a tuple, a dict, etc, but only one thing
>>> def fun():
return 1,"boo",[1,2,3],{1:10,3:23}
>>> fun()
(1, 'boo', [1, 2, 3], {1: 10, 3: 23})
>>>
in this example it return a tuple with all that stuff because , is the tuple constructor, so it make a tuple first (your one thing) and return it

Obtain sum of subsets using recursion

I am working on a code to identify subsets of an array with sum of the elements in subsets equal to a given number, k. Here is the recursive code I attempted.
First approach:
def subset_sum(arr,curr,k,idx):
if idx==len(arr):
print(curr) //print the current subset for debugging
if sum(curr)==k:
print(curr)
return
subset_sum(arr,curr,k,idx+1) //exclude the element
curr.append(arr[idx]) //include the element
subset_sum(arr,curr,k,idx+1) // make recursive call
Then this function has been called using;subset_sum([1,2,3],[],4,0), and we get following output;
[]
[3]
[3]
[3, 2]
[3, 2, 3]
[3, 2, 3, 1]
[3, 2, 3, 1, 3]
[3, 2, 3, 1, 3, 2]
[3, 2, 3, 1, 3, 2, 3]
It is being difficult to comprehend, why are the elements being duplicated above. After some brainstorming, I tried second approach below.
Second approach.
def subset_sum(arr,curr,k,idx):
cnt=0
if idx==len(arr):
if sum(curr)==k:
cnt+=1
print(curr,cnt)
return cnt
subset_sum(arr,curr,k,idx+1)
subset_sum(arr,curr+[arr[idx]],k,idx+1)
We call the function subset_sum([1,2,3],[],3,0,0). This gives the output as;
[1,2],1
[3],1
since 1+2 gives us the required sum 3. May I know what is wrong in updating the element curr as I did in the first approach; curr.append(arr[idx]). And why is that the second approach is working fine.

Understanding the shallow copy in Python [duplicate]

This question already has answers here:
What is the difference between shallow copy, deepcopy and normal assignment operation?
(12 answers)
Closed 2 years ago.
I have two sets of code which demonstrate shallow copy but I am not able to explain why the code behaves differently.
The first set of code:
import copy
cv1 = [1,2,3]
cv2 = copy.copy(cv1)
print(cv1)
print(cv2)
cv2[0] = 0
cv1[1] = 1
print(cv1)
print(cv2)
The output :
[1, 2, 3]
[1, 2, 3]
[1, 1, 3]
[0, 2, 3]
Second set of code:
import copy
a = [ [1, 2, 3], [4, 5, 6] ]
b = copy.copy(a)
print(a)
print(b)
a[1][2] = 25
b[0][0] = 98
print(a)
print(b)
The output :
[[1, 2, 3], [4, 5, 6]]
[[1, 2, 3], [4, 5, 6]]
[[98, 2, 3], [4, 5, 25]]
[[98, 2, 3], [4, 5, 25]]
In my understanding, both codes should do the exact same thing. Why is that after the second set of print statements in each code snippet, the contents of cv1 and cv2 are different while a and b are the same.? Maybe it is a very basic error on my side, I am new to Python, but I can't seem to figure this out. Any help is appreciated.
This has to do with the copy library you imported.
copy.copy(x)
Return a shallow copy of x.
A shallow copy constructs a new compound object and then (to the extent possible) inserts > references into it to the objects found in the original.
So in the first case the copy is creating a new list of int object while the second case it is creating a list of references object.
While in the second case the list a and b are different, they contain the same lists inside. Thats why changing one of those list inside will edit both lists.
For the two cases to be the same you need to use the copy.deepcopy function.

How to get a list of objects by maximum attribute value from N multi-list of objects?

I have a multi-list of objects as follows (simplified version)
listA = [[obj1(val=1),obj2(val=1)],[obj2(val=4),obj3(val=2)]]
listB = [[obj4(val=1),obj5(val=1)],[obj6(val=5),obj7(val=3)]]
listC = [[obj8(val=1),obj9(val=1)],[obj10(val=6),obj11(val=4)]]
I want to get a list of objects from the above multi-list which has the maximum value of a certain attribute by comparing the sub-lists of each multi-list. If the value of the attribute is the same for all the compared objects, it should get any one object.
output:
maxList = [obj1(value=1),obj10(val=6)]
There is a similar question to get object with maximum value of attribute from a list, but this case is for multi-list. I know this can be acheived with nested for loops, but there a must be a better way to do this with itertools and getattr ?
To simplify, let's demonstrate on regular integers. Adapt this approach to your object.
Given
import itertools as it
a = [[1, 1], [3, 2]]
b = [[1, 1], [5, 3]]
c = [[1, 1], [6, 3]]
Code
list(map(max, [list(it.chain(*col)) for col in zip(a, b, c)]))
# [1, 6]
Equivalently
[max([x for x in it.chain(*col)]) for col in zip(a, b, c)]
# [1, 6]

How would I iterate through a list and replace objects that are repeated?

I am trying to go through a list and have each object in that list compared with the others, and all repetitions of it replaced with something else.
>>> t = [1, 2, 1, 1, 2, 2, 4, 4]
>>> for i in range(len(t)):
num = t[i]
if num in t[i+1:]:
num = 'cherry'
This is not turning the repeated ints into 'cherry'. I know that I am referring to them correctly as I put print(num) in place of num = cherry and it is printing what I want. It will not reassign them, though. What am I doing wrong?
You should make the list a set because sets are unordered collections of unique elements and are great for removing duplicates from a sequence
To make a set use either the set() function like in the code below or use curly braces {} like the output
t = [1, 2, 1, 1, 2, 2, 4, 4]
x = set(t)
print(x)
#Output
{1, 2, 4}

Resources