python - modifying an item inside an array using a function - python

I am new to python. Can someone please explain to me how this works in python?
How to change the value of item in the main function?
def calc(arr = []):
index = 0
for item in arr:
item = item + 1
arr[index] = item
index += 1
if __name__ == '__main__':
item = 1
calc([item])
print("item is => ", item)

If your aim is to change an immutable value in some way, this would be best:
def add_one(value):
return value + 1
def main():
# your code moved to a function, to avoid these variables inadvertently becoming globals
item = 1
# update item with the function result
item = add_one(item)
print("item is => ", item)
if __name__ == '__main__':
main()
From your example, it appears you want to update a list with each item in the list incremented by 1, you can do that the same way:
def add_one_all(values):
return [values + 1 for values in values]
def main():
items = [1, 4, 9]
# update items with the function result
items = add_one_all(items)
print("items are => ", items)
if __name__ == '__main__':
main()
However, since a list is mutable, you can update it from inside a function, by making the changes to the list in-place:
def add_one_all_inplace(values: list = None):
if values is not None:
for i in range(len(values)):
values[i] += 1
def main():
items = [1, 4, 9]
# update the content of items
add_one_all_inplace(items)
print("items are => ", items)
if __name__ == '__main__':
main()
The advantage of the latter solution is that no new list is created, which may be preferable if you need to be very frugal with space, or are only making a few changes to a very large list - your example would probably be better served with the second solution though.
Note that the way you called the function still wouldn't work in the latter case:
def main():
item = 1
add_one_all_inplace([item])
The list containing item would be changed to be [2], but that doesn't affect item itself. The list passed to add_one_all_inplace will just contain the value of item, not a reference to it.

you can do with the global keyword in python which allows us to use a non-global variable as global if we get an error like 'UnboundLocalError: local variable '' referenced before assignment' in that case we also use the global keyword I have done the same code with proper assignment in the below image
as you have declared variable in the wrong place because that would only be accessible inside the if block only so you have to place the variable at the upper level and what will happen if I don't use the global keyword see the below image
you can see the difference
and if you like my answer please follow me and upvote my answer

Related

Recursive behavior

Why does the following executes such that the print statement is called as often as it recursed but the count variable, count, when x == 1 is never reached.
def count_bits(n, count = 0):
x = n % 2
if n == 1:
return count + 1
if n < 1:
return count
if x == 1:
count += 1 # when x == 1
count_bits(int(n/2), count)
print("counter")
return count
why is it necessary to recurse with the return statement? Because if the recursive call is above the return statement the code
returns the wrong output but with the recursive call called with
return keyword, everything works well. Typically, the print statement
prints 'counter' as often as it recursed showing that the recursive call
works.
On the other hand, if "return" follows after the recursive call, it returns the count from the base condition, correctly.
def count_bits(n, count = 0):
x = n % 2
if n == 1:
return count + 1
if n < 1:
return count
if x == 1:
count += 1
return count_bits(int(n/2), count)
You have to return recursion result, as reccurent function meaning is to count current step you have to get result of previous step
F_k = F_k-1 * a + b # simple example
Means that you have to get result of F_k-1 from F_k and count current result using it.
I advised you to use snoop package to debug your code more efficient , by this package you can track the executing step by step.
to install it run:
Pip install snoop
Import snoop
add snoop decorator to count_bits()
for about the package see this link
https://pypi.org/project/snoop/
the difference in the output between the two methods is because of the way in which python handles fundamental data types. Fundamental data types such as float, ints, strings etc are passed by value, whereas complex data types such as dict, list, tuple etc are passed by reference. changes made to fundamental data types within a function will therefore only be changed within the local scope of the function, however changes made to a complex data type will change the original object. See below for an example:
x = 5
def test_sum(i : int):
print(i)
i += 5
print(i)
# the integer value in the global scope is not changed, changes in test_sum() are only applied within the function scope
test_sum(x)
print(x)
y = [1,2,3,4]
def test_append(l : list):
print(l)
l.append(10)
print(l)
# the list in the global scope has '10' appended after being passed to test_append by reference
test_append(y)
print(y)
This happens because it's far cheaper computationally to pass a reference to a large object in memory than to copy it into the local scope of the function. For more information on the difference, there are thousands of resources available by searching "what is the difference between by reference and by value in programming".
As for your code, it seems to me the only difference is you should alter your first snippet as follows:
def count_bits(n, count = 0):
x = n % 2
if n == 1:
return count + 1
if n < 1:
return count
if x == 1:
count += 1 # when x == 1
# assign the returned count to the count variable in this function scope
count = count_bits(int(n/2), count)
print("counter")
return count
The second code snippet you wrote is almost identical, it just doesn't assign the new value to the 'count' variable. Does this answer your question?

Changing(replacing) a value in a list without using range(len())

My purpose is to change the value of the elements 3 and 4 to 4 and 3 and I have written a function that takes a list, first number and second number as arguments:
def pre9(the_list, value_to_replace, the_replacing_value):
for i in the_list:
if i == value_to_replace:
value_to_replace = the_replacing_value
elif i == the_replacing_value:
the_replacing_value = value_to_replace
return the_list
I then assign a test-case to a variabel and then print it:
test_pre9 = pre9([1,2,3,4,5,7,3,4], 3, 4)
print(test_pre9)
The result is: [1,2,3,4,5,7,3,4]
I expect it to be: [1,2,4,3,5,7,4,3]
I have for a long time ago written a code that accoplishes this task:
def uppgift_9():
the_list = [3,5,8,9,4,5]
for i in range(len(the_list)-1):
temp = the_list[3]
the_list[3] = the_list[4]
the_list[4] = temp
return the_list
But I've read in many places that using range(len()) is not "pythonic" and it is possible to do anything without using it.
Does anyone know why my code fails?
You don't actually change the item in the list, try this:
def pre9(the_list, value_to_replace, the_replacing_value):
for i, value in enumerate(the_list):
if value == value_to_replace:
the_list[i] = the_replacing_value
elif value == the_replacing_value:
the_list[i] = value_to_replace
return the_list
Now the list will have the actually items changed to what you wanted it to be. Enumerate() returns the index and value of an item in a list, it's very handy! And indeed, the range(len()) is not very pythonic and is usually used when people jump from other languages like Java, C# etc. Using enumerate() is the correct 'pythonic' way of achieving this.

Function in Python is treating list like a global variable. How to fix this?

I'm not sure why l would be modified by the find() function. I thought since I'm using a different variable in another function, l would not be modified by the function since it's not global.
I made sure it wasn't an error in the code by copy and pasting l = [2, 4, 6, 8, 10] before every print statement, and it returned the right outputs, meaning l is being changed by the function. I also removed the main function from the main and basically made it outright global, but it still gave the original bad results.
I'm not sure if this is an issue with my understanding of Python since I'm a beginner in it and I'm coming from Java.
Here's the code and results:
def find(list, user):
while True:
n = len(list)
half = int(n/2)
if n == 1:
if user != list[0]:
return "Bad"
else:
return "Good"
elif user == list[half]:
return "Good"
elif user > list[half]:
del list[0:half]
elif user < list[half]:
del list[half:n]
print(list)
if __name__ == "__main__":
l = [2, 4, 6, 8, 10]
print(find(l, 5)) # should print Bad
print(find(l, 10)) # should print Good
print(find(l, -1)) # should print Bad
print(find(l, 2)) # should print Good
but it returns with this
[2, 4]
[4]
Bad
Bad
Bad
Bad
You should read this question at first. why can a function modified some arguments while not others.
Let me rewrite your code for clarification.
def find(li, el):
# li is a list, el is an integer
# do something using li and el
if __name__ == "__main__":
l = [1,2,3,4]
e = 2
find(l, e)
The function find received two objects as parameters, one is li and the other is el. In main, we defined two objects, a list, we called it l, and an integer, we called it e. Then these two objects was passed to find. It should be clear that it is these two objects that passed to the function, not the name. Then your find function has access to this object, called l in main, while called li in find. So when you change li in find, l changed as well.
Hope that answers your question. And to fix this, check deepcopy.
Arguments in Python are passed by assignment. https://docs.python.org/3/faq/programming.html#how-do-i-write-a-function-with-output-parameters-call-by-reference
In your case, that means that the list parameter of your find function is assigned the exact same list you pass in as the argument l. So, when you modify list (which is a very bad name since it shadows Python's list keyword), you also modify l, since no copy of the original was made.
You could use copy() to pass in a copy, but I think you would do well to reconsider the function as a whole, since it currently has many, many issues and you're likely to end up with a solution that won't suffer from having the original list passed in.

Global variable for recursion in Python

I am facing some difficulties in backtracking.
How do I define a global list to be used in the problems of backtracking? I saw a couple of answers and they have all suggested using 'global' keyword in front of the variable name, inside the function to use as global. However, it gives me an error here.
Is there any good general method that can be used to get the result, instead of a global variable?
The code below is trying to solve the backtrack problem wherein, a list of numbers is given, and we have to find unique pairs (no permutations allowed) of numbers that add to the target.
For example, given candidate set [2, 3, 6, 7] and target 7,
A solution set is:
[
[7],
[2, 2, 3]
]
///////////////////////////////CODE/////////////////////////////
seen = []
res = []
def func(candidates, k, anc_choice): #k == target
#global res -- gives me an error -- global name 'res' is not defined
if sum(anc_choice) == k:
temp = set(anc_choice)
flag = 0
for s in seen:
if s == temp:
flag = 1
if flag == 0:
seen.append(temp)
print(anc_choice) #this gives me the correct answer
res.append(anc_choice) #this doesn't give me the correct answer?
print(res)
else:
for c in candidates:
if c <= k:
anc_choice.append(c) #choose and append
if sum(anc_choice) <= k:
func(candidates, k, anc_choice) #explore
anc_choice.pop() #unchoose
func(candidates, k, [])
Can someone please provide me answers/suggestions?
To utilise the global keyword, you first need to declare it global before instantiating it..
global res
res = []
Though from looking at your code. Because the res = [] is outside the function, it is already available globally.
There are plenty of reasons why you should not use global variables.
If you want a function that updates a list in the above scope, simply pass the list as argument. Lists are mutable, so it will have been updated after the function call.
Here is a simplified example.
res = []
def func(candidate, res):
res.append(candidate)
func(1, res)
res # [1]

function would not change the parameter as wanted

here is my code
def common_words(count_dict, limit):
'''
>>> k = {'you':2, 'made':1, 'me':1}
>>> common_words(k,2)
>>> k
{'you':2}
'''
new_list = list(revert_dictionary(count_dict).items())[::-1]
count_dict = {}
for number,word in new_list:
if len(count_dict) + len(word) <= limit:
for x in word:
count_dict[x] = number
print (count_dict)
def revert_dictionary(dictionary):
'''
>>> revert_dictionary({'sb':1, 'QAQ':2, 'CCC':2})
{1: ['sb'], 2: ['CCC', 'QAQ']}
'''
reverted = {}
for key,value in dictionary.items():
reverted[value] = reverted.get(value,[]) + [key]
return reverted
count_dict = {'you':2, 'made':1, 'me':1}
common_words(count_dict,2)
print (count_dict)
what i expected is to have the count_dict variable to change to {'you':2}.
It did work fine in the function's print statement, but not outside the function..
The problem, as others have already written, is that your function assigns a new empty dictionary to count_dict:
count_dict = {}
When you do this you modify the local variable count_dict, but the variable with the same name in the main part of your program continues to point to the original dictionary.
You should understand that you are allowed to modify the dictionary you passed in the function argument; just don't replace it with a new dictionary. To get your code to work without modifying anything else, you can instead delete all elements of the existing dictionary:
count_dict.clear()
This modifies the dictionary that was passed to the function, deleting all its elements-- which is what you intended. That said, if you are performing a new calculation it's usually a better idea to create a new dictionary in your function, and return it with return.
As already mentioned, the problem is that with count_dict = {} you are not changing the passed in dictionary, but you create a new one, and all subsequent changes are done on that new dictionary. The classical approach would be to just return the new dict, but it seems like you can't do that.
Alternatively, instead of adding the values to a new dictionary, you could reverse your condition and delete values from the existing dictionary. You can't use len(count_dict) in the condition, though, and have to use another variable to keep track of the elements already "added" to (or rather, not removed from) the dictionary.
def common_words(count_dict, limit):
new_list = list(revert_dictionary(count_dict).items())[::-1]
count = 0
for number,word in new_list:
if count + len(word) > limit:
for x in word:
del count_dict[x]
else:
count += len(word)
Also note that the dict returned from revert_dictionary does not have a particular order, so the line new_list = list(revert_dictionary(count_dict).items())[::-1] is not guaranteed to give you the items in any particular order, as well. You might want to add sorted here and sort by the count, but I'm not sure if you actually want that.
new_list = sorted(revert_dictionary(count_dict).items(), reverse=True)
just write
return count_dict
below
print count_dict
in function common_words()
and change
common_words(count_dict,2)
to
count_dict=common_words(count_dict,2)
So basically you need to return value from function and store that in your variable. When you are calling function and give it a parameter. It sends its copy to that function not variable itself.

Categories

Resources