How does call by reference in Python work? - python-3.x

Please explain when calling function first time the code follows call by reference. But when calling function second time it does not follow the same.
##functions
num = [0,1,2,3,4]
def first(num1):
num1.append([5, 6]);
print(num1)
def second(num1):
num1 = num1 + [7, 8];
print(num1)
print(num)
first(num)
print(num)
second(num)
print(num)

In first the argument is passed by reference and modified inside the function using that reference.
In second the name num1 is used but also assigned a new value. The num1 is bound to a new value and this does not change the original passed in.

In first method you are appending a list to num1 that basically points to num at that point, So basically it refers to the same location while in second method you are adding a list to num1 which create a new location.
You can understand better by using the function id() which basically gives a unique integer value for every object.
##functions
num = [0,1,2,3,4]
def first(num1):
print(id(num1)) # obj reference
num1.append([5, 6]);
print(id(num1))
print(num1)
def second(num1):
print(id(num1))
num1 = num1 + [7, 8];
print(id(num1))
print(num1)
print(num)
first(num)
print(num)
second(num)
print(num)
If you run the above code, you will find that the object reference is same in first method before and after operation, while in second method it is different.
OUTPUT:
[0, 1, 2, 3, 4]
140360682379784
140360682379784
[0, 1, 2, 3, 4, [5, 6]]
[0, 1, 2, 3, 4, [5, 6]]
140360682379784
140360672905160
[0, 1, 2, 3, 4, [5, 6], 7, 8]
[0, 1, 2, 3, 4, [5, 6]]
And also this article would help you understand better about the concept of call by value and call by reference in python.
Is Python call-by-value or call-by-reference? Neither
You can try the code here: Code

Related

Default function values and variables in memory

I came across the following code:
def f(x,l=[]):
for i in range(x):
l.append(i*i)
print(l)
f(2)
f(3,[3,2,1])
f(3)
which returns:
[0, 1]
[3, 2, 1, 0, 1, 4]
[0, 1, 0, 1, 4]
my question is this:
Why, in the third function call f(3), is l not overwritten with l=[] in def (f(x,l=[]) and then return [0,1,4]? What is going on here??
When I do this instead, things work as expected..:
l=[]
[l.append(i*i) for i in range(2)]
print(l)
l=[1, 2, 3]
[l.append(i*i) for i in range(3)]
print(l)
l=[]
[l.append(i*i) for i in range(3)]
print(l)
which prints:
[0, 1]
[1, 2, 3, 0, 1, 4]
[0, 1, 4]
I know the answer has to do with the way python stores data in memory, but I can't wrap my head around this one, even after exploring with id(l) (which prints out the same ID of l for both f(2) and f(3) but a different ID for f(3, [3,2,1]).
Thank you for any insight!
The correct way of doing this is to have a local variable lst inside the function which takes care of the operation you are doing inside the function, rather than operating on a variable you are passing like so.
def f(x,lst=None):
lst = lst or []
for i in range(x):
lst.append(i*i)
print(lst)
f(2)
f(3,[3,2,1])
f(3)
Which gives you output as
[0, 1]
[3, 2, 1, 0, 1, 4]
[0, 1, 4]
The reason the previous approach doesn't work is because when a mutable value as list or dictionary is detected in a default value for an argument, they are evaluated only once at function definition time, which means that modifying the default value of the argument will affect all subsequent calls of the function.
But in my changed function, I am making a new local variable to operate on the list, which is a copy of the argument passed, so we don't get this issue here.

parsing infinite list into one list

I have this task building a code using recursion. The task is taking a list who can have an infinite amount of lists inside it and making it one list.
This is so far what I have:
def flat_list(array):
new_array =[]
for i in range(0,len(array)):
if len(str(array[i])) > 1:
flat_list(array[i:i+1])
else:
new_array += array[i:len(str(array))-1]
return new_array
These are the tests it needs to pass:
assert flat_list([1, 2, 3]) == [1, 2, 3]
assert flat_list([1, [2, 2, 2], 4]) == [1, 2, 2, 2, 4]
assert flat_list([[[2]], [4, [5, 6, [6], 6, 6, 6], 7]]) == [2, 4, 5, 6, 6, 6, 6, 6, 7]
assert flat_list([-1, [1, [-2], 1], -1]) == [-1, 1, -2, 1, -1]
Mine returns this:
flat_list([1, [2, 2, 2], 4])
my result: [1,[2,2,2],4]
right answer: [1,2,2,2,4]
I think my problem is with creating a new local variable of the new_array at each entry, How can I return one list with no other lists inside it?
This task is without using numpy, but if you can also show me how it can be done with numpy it will really educate me. :)
Thank you for answering
Try this:
def flatten(S):
if S == []:
return S
if isinstance(S[0], list):
return flatten(S[0]) + flatten(S[1:])
return S[:1] + flatten(S[1:])
How it works:
1. The list is passed as an argument to a recursive function to flatten the list.
2. In the function, if the list is empty, the list is returned.
3. Otherwise the function is recursively called with the sublists as the parameters until the entire list is flattened.
I suggest you the following suggestion: it iterates over the list and if the encountered item is a list, then it recursively flattens it before appending it to the resulting flattened list:
def flat_list(aList):
result = []
for i in aList:
if isinstance(i, list):
result += flat_list(i)
else:
result.append(i)
return result

How to write a function where the original list of integers is changed without using return?

Let us say we have a list of integers:
list = [6, 4, 1, 4, 4, 4, 4, 4, 2, 1]
I now wrote a function which returns another list with all the integers from the list above without repeats.
def no_repeats(s):
new_list = []
for number in s:
if new_list.count(number) < 1:
new_list.append(number)
return(new_list)
The new_list returns [6, 4, 1, 2] which is good! My question is how I would now write two similar functions:
A function clean(s) which does not return a new list like the function above, but changes the original list by deleting all the numbers that repeat. Thus, the result has to be the same and the function must not include "return" or create a new list. It must only clean the original list.
A function double(s) which, again, changes the original list (does not return a new list!) but this time, by doubling every number in the original list. Thus, double(list) should change the original list above to:
[6, 6, 4, 4, 1, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 1, 1]
Thank you for all the help!
Removing duplicates inplace without preserving the order:
def no_repeats(L):
L[:] = set(L)
There are several variations possible (preserve order, support non-hashable items, support item that do not define total ordering) e.g., to preserve order:
from collections import OrderedDict
def no_repeats(L):
L[:] = OrderedDict.fromkeys(L)
To double each element's value inplace:
def double(L):
for i in range(len(L)):
L[i] *= 2
To duplicate each element:
def duplicate_elements(L):
L[:] = [x for x in L for _ in range(2)]
>>> def clean(s):
... s[:] = [s[i] for i in range(len(s)) if s[i] not in s[:i]]
...
>>> st = [1, 2, 3, 2, 1]
>>> clean(st)
>>> st
[1, 2, 3]
>>> def double(s):
... s[:] = [s[i//3] for i in range(3*len(s)) if i % 3]
...
>>> st = [1, 2, 3, 2, 1]
>>> double(st)
>>> st
[1, 1, 2, 2, 3, 3, 2, 2, 1, 1]
neither is particularly efficient nor pythonic, yet do address the OP question
def double(s):
... s[:] = [s[i//2] for i in range(2*len(s))]
will also do the trick, with a little less obsfucation

How to find all numbers in a list that are not part of a pair - using python 3

I am trying to write a python 3 function that finds all numbers in a list (unspecified length) that are not part of a pair.
For example, given the list [1, 2, 1, 3, 2], the function will return 3; and given the list [0, 1, 1, 7, 8, 3, 9, 3, 9], the function will return 0, 7, and 8.
Thanks for your help!
You can use the following function :
>>> def find(l):
... return (i for i in l if l.count(i)==1)
>>> l= [0, 1, 1, 7, 8, 3, 9, 3, 9]
>>> list(find(l))
[0, 7, 8]
This function will return a generator that is contain the elements in list which those count is equal to 1.
I can tell you how I would do it. What does it mean a "pair"?
You should say, find all the numbers repeated oddly in the array.
First plan: (more efficient!)
Sort the list and then a single loop through your list should be enough to find how many numbers of each there are inside and you can generate awhile another list that you will return.
Second plan (nicer in python, but also more expensive because of the number of evaluations though the hole list):
Try the solution of Kasra. 'count' function from 'list' type helps our code but not our efficiency. It counts the number of times that appears the value 'i' on the list 'l', obviously.
If the pair need to be "closed pair" I mean, if you have three 1 (ones), do you have one pair and one single 1? or do you have all the 1 paired? If the second one, the solution of Kasra is Ok. Else you should compare:
if l.count(i) % 2 == 1
This can be easily and efficiently done in 3 lines with collections.Counter.
from collections import Counter
def unpaired(numbers):
for key, count in Counter(numbers).items():
if count % 2:
yield key
print(list(unpaired([1, 2, 1, 3, 2])))
# [3]
print(list(unpaired([0, 1, 1, 7, 8, 3, 9, 3, 9])))
# [0, 7, 8]
My answer comport if you have three equals numbers or if you have one pair and one single number without pair.
def name(array):
o = sorted(array)
c = []
d = []
for i in o:
if o.count(i) % 2 == 1:
c.append(i)
for j in c:
if j not in d:
d.append(j)
return d
or do not use for j in c and use directly:
return list(set(c))
for example:
array = [0, 1, 1, 7, 8, 3, 9, 3, 9, 9]
output: [0, 7, 8, 9]

How to use iter and next in a function to return a list of peaks?

Problem:
I need to define a peaks function which passed to an iterable as parameter, by computing this iterable, the function should return a list of peaks. The only data structure that I can create is the list is returning; use no intermediate data structures to help the computation: e.g., I cannot create a list with all the values in the iterable. Note that I also cannot assume the argument is indexable, nor can I compute len for it: it is just iterable.
For example:
peaks([0,1,-1,3,8,4,3,5,4,3,8]) returns [1, 8, 5].
This result means the values 1, 8, and 5 are strictly bigger than the values immediately preceding and following them.
def peaks(iterable):
l=[]
i=iter(iterable)
v=next(i)
try:
while True:
if v>next(iter(iterable)):
l.append(v)
v=next(i)
except StopIteration:
pass
return l
Calling these should give me:
peaks([0,1,-1,3,8,4,3,5,4,3,8]) --> [1,8,5]
peaks([5,2,4,9,6,1,3,8,0,7]) -->[9,8]
But I am getting:
peaks([0,1,-1,3,8,4,3,5,4,3,8]) --> [1, 3, 8, 4, 3, 5, 4, 3, 8]
peaks([5,2,4,9,6,1,3,8,0,7]) --> [9, 6, 8, 7]
Please help me with this problem, I have spent so much time on it and am making no progress on it. And, I don't know how to write the if statement to check the values immediately preceding and following. Any helps would be great! Actual codes would be really appreciated since my English is bad.
def peaks(L):
answer = []
a,b,c = itertools.tee(L, 3)
next(b)
next(c)
next(c)
for first, second, third in zip(a,b,c):
if first <= second >= third:
answer.append(second)
return answer
In [61]: peaks([0,1,-1,3,8,4,3,5,4,3,8])
Out[61]: [1, 8, 5]
In [62]: peaks([5,2,4,9,6,1,3,8,0,7])
Out[62]: [9, 8]

Resources