calling to function in function - python

I need to build 3 funcs.
the 1st is insertion sort, 2nd is generate list of random nums between 0-1 and the 3rd need to create list of randon numbers (using 2nd func.) and sort them (using 1st func.).
I can't change the tests in the end and not the arguments of the funcs.
I have a problem with func 3, it says NameError: global name 'my_list' is not defined.
The other funcs works fine, where am I wrong in the 3rd?
*the 1st func not allowed to return anything.
thanks!
my code:
def insertion_sort(lst):
for i in range(1,len(lst)):
current=lst[i]
j=i-1
while j>=0:
if current < lst[j]:
lst[j+1] = lst[j]
lst[j] = current
j-=1
else:
break
def random_list(n):
import random
my_list=[]
for i in range(n):
my_list.append (random.random ())
return my_list
def sorted_random_list(n):
random_list(n)
insertion_sort(my_list)
### TEST FUNCTION - DON'T CHANGE THIS ####
def test_sorted(lst):
print lst == sorted(lst) and len(lst) > 0
def test_len(lst, length):
print len(lst)==length
def sort_and_test(lst):
lst = lst[:]
insertion_sort(lst)
test_sorted(lst)
sort_and_test([13,54,3434,88,334,6,8,84,57,4,2,4,6,6])
sort_and_test(["dog","cat","cow","zebra","frog","bat","spider","monkey"])
sort_and_test(["hi","hello","how are you","what your doing"])
test_len(random_list(10), 10)
lst = sorted_random_list(10)
test_len(lst, 10)
test_sorted(lst)

It's because you call random_list(n) but you don't assign the value it returns to any variable.
Try this instead:
my_list = random_list(n)
And that should solve your problem
Without assigning the return value of random_list() to a variable, all you're doing is computing random_variable() and throwing away the result. What you want to do is to name the result of the call to random_variable() so that you can refer to it later. This is done by assigning it to a variable with that name, in this case, my_list
Hope this helps

Well, the quickest way is to replace the code in sorted_random_list() with
new_list = random_list(n)
insertion_sort(new_list)
return new_list
but the deeper problem here appears to be a lack of understanding of scoping and the need for assignment of values to variables. my_list only exists within the scope of random_list(), so it is not available in the global namespace, which is why you are getting the error you are seeing here. You also aren't actually assigning the result of random_list(n) to anything, so you are throwing away the new list. Then you don't actually return the list created in sorted_random_list() in any case.

my_list is not a global variable. Because you are returning the list from random_list(), in sorted_random_list() you should have my_list = random_list(n). This will create the variable my_list within the scope of that function.

Related

Confused about the scope of variable in python [duplicate]

This question already has answers here:
Why can a function modify some arguments as perceived by the caller, but not others?
(13 answers)
Closed 6 months ago.
I was studying merge sort and I wrote this code
def mergesort(a): #sorting algo
#code is alright just confused about scope of variable
if(len(a)>1):
mid = len(a)//2
l = a[:mid]
r = a[mid:]
mergesort(l)
mergesort(r)
i=j=k=0
while(i<len(l) and j<len(r)):
if(l[i]>r[j]):
a[k] = r[j]
j+=1
else:
a[k] = l[i]
i+=1
k+=1
while(i<len(l)):
a[k] = l[i]
i+=1
k+=1
while(j<len(r)):
a[k] = r[j]
j+=1
k+=1
a = [30,2,4,5,-1,3,7,3,9,2,5]
mergesort(a)
print(a)
here a is a global variable, but when the function parameter name is also a doesn't the a in function scope override the a in the global scope? how the global variable is getting edited inside mergesort function?
I tried another code with same approach but here the global variable doesn't get edited(as expected),how is this different from the above code?
def foo(a):
a=20
a = 10
foo(a)
print(a)
The problem is not that a is global (because it's not). The a is local to the function, but it references the same list as the global a (because variables are passed by reference in python).
a is a list (which is mutable) in the first example, while a is an integer (which is immutable) in the second example. So changing values inside of the list (like a[k] = l[i]), changes the global list as well, while changing the reference to an integer (like a = 20) only replaces the local reference.
If you don't want the function to change the global list, you can fix it by creating a copy of the list:
def mergesort(a):
a = a[:] # or a = a.copy(), whichever you prefer
...
Note that in this case, the function has no effect, so you'd probably want to return the resulting list.
Lists are mutable but int is not. So when you change int, you get an entirely new int. When you make changes within a list, you don't get a new list. You just have a modified list.
So when you pass the list to a function, it does not create a new list unless you do something like this: a.copy() or a[:] which creates a new list. In your case, you are using the same list as outside the function.
For more info read about mutability of data types or pass by reference and pass by value.

Making an empty list within a function and passing it to global scope. (initialising empty list in function)

I want to make a function that takes makes a list and adds an item to that list when I run it, so basically I would be passing this function two arguments, the first is the name I want the list to have, the second is an item I want to add to the list.
I want to do this as a learning excercise and I've built a function that almost does what I want it to do.
def addlst(l, item):
"""add an item to a list"""
l = list()#this line essentially does nothing.
if type(item) == str:
l.append(item.capitalize())
return l
print(l)
else:
l.append(item)
return l
print(l)
if I pass this something like:
addlst(people, 'kev')
I get the error:
NameError: name 'people' is not defined
but obviously, if I define people as an empty list it works fine.
Is what I'm doing actually possible? I know that as it stands the line
l = list()
would just empty the list first and so the append function would be useless (I'd have to add another clause to check if the list exists already) but my question is really about initialising a blank list within a function and then returning it to the global scope.
Putting aside the discussion regarding whether it is a good practice, (which can make sens if your main goal is about improving your understanding), you could simply use the global keyword to do what you describe. Say
def f(el):
global l
l.append(el)
Then
>>> l = []
>>> f(2)
>>> l
[2]
>>> f(3)
>>> l
[2, 3]
As it reads above, l has to be declared before using f.
Dealing with your peculiarities, something you could do is:
def addlst(item):
"""add an item to a list"""
global l#
if isinstance(item, str): # type(item) == str is not recommanded
item = item.capitalize()
l.append(item)
But actually, note that doing so will "bind" your function to deal exclusively with the list named l in the global scope. And it looks like this is not what you want, since it appears that you want to be able to pass multiple list objects to your function. The best approach here is
def addlst(list_, item):
"""add an item to a list"""
if isinstance(item, str):
item = item.capitalize()
list_.append(item)
return list_
First off: a function should never inject a new name into the calling scope.
If the function works with a global variable, that needs to be documented and the caller has to ensure the global exists before calling it.
If the function takes an argument, there are two options. One, you can mutate it and have your function return None, or you can create a new value based on the argument and return that, leaving the argument unchanged. Very rarely, if ever, should your function modify an argument and return a value.
If you have your function return a new list, you can optionally take a list to modify, or create a brand new list inside your function.
Unrelated, but you shouldn't care what the type of item is, only that it is something that has a capitalize method that you can call. Just try it; if it doesn't, it will raise an AttributeError that you can catch, in which case you can simply use item as is.
Putting all this together, I recommend the third approach. add_to_list will take an item as the first argument, and an optional list as the second argument. If no list is given, the function will create a new list. In either case, you'll append the appropriately modified item to the list and return it.
def add_to_list(item, l=None):
# Note: this doesn't change the item you pass; it just rebinds
# the local name item to a *new* string if it succeeds.
try:
item = item.capitalize()
except AttributeError:
pass
if l is None:
l = []
return l + [item]
Then you can use
people = add_to_list('kev') # people == ['Kev']
people = add_to_list('bob') # people == ['Bob'], Kev is gone!
people = add_to_list('kev', people) # people == ['Bob', 'Kev'], Kev is back.
The more efficient version mentioned in the second approach modifies l in place; in this case, though, you have to provide a list; you can't create a new list.
def add_to_list(item, l):
try:
item = item.capitalize()
except AttributeError:
pass
l.append(item)
people = [] # Create the list in the *calling* scope, not in add_to_list
add_to_list('kev') # TypeError, missing an argument
add_to_list('kev', people) # people == ['Kev']
add_to_list('bob', people) # people == ['Kev', 'Bob']
The first approach is pretty poor; it restricts your function to working with a specific list whose name is hard-coded in the function, but I'll mention it here for completeness. Since the list is hard-coded, we'll change the name of the function to reflect that.
def add_to_people(item):
global people
try:
item = item.capitalize()
except AttributeError:
pass
people.append(item)
Now add_to_list can work with the global list people, but no other list.
people = []
add_to_people('kev')
add_to_people('bob')
And finally, in the interest of full disclosure, yes, add_to_people can create the list if it hasn't already:
def add_to_people(item):
global people
try:
people # Simply try to evaluate the name
except NameError:
people = []
# ...
However, if using a global name in the first place is bad, autovivifying it like this is worse. Avoid this style of programming wherever possible.
When you write addlst(people, 'kev'), you're telling your code that you want to execute the addlst function with a variable named people as first parameter.
Problem is: you never set this variable!
There are many ways to this; You could either initialize an empty list before calling the function:
people = []
addlst(people, 'kev')
Or make the parameter optional with a default value:
def addlst(item, l = None):
"""add an item to a list"""
if l is None:
l = []
if type(item) == str:
l.append(item.capitalize())
return l
else:
l.append(item)
return l
But that could be tricky, since lists are mutable objects.
NB: In your function, print will never be called because it stands after the return statement.
A last way
Eventually, you could also shorten your code by doing something like that:
mylist = []
item = "test"
mylist.append(item.capitalize() if type(item) == str else item)

Why function changes global list and not global integer with the same code [duplicate]

This question already has answers here:
Why can a function modify some arguments as perceived by the caller, but not others?
(13 answers)
Closed 3 years ago.
Let's take a simple code:
y = [1,2,3]
def plusOne(y):
for x in range(len(y)):
y[x] += 1
return y
print plusOne(y), y
a = 2
def plusOne2(a):
a += 1
return a
print plusOne2(a), a
Values of 'y' change but value 'a' stays the same. I have already learned that it's because one is mutable and the other is not. But how to change the code so that the function doesn't change the list?
For example to do something like that (in pseudocode for simplicity):
a = [1,2,3,...,n]
function doSomething(x):
do stuff with x
return x
b = doSomething(a)
if someOperation(a) > someOperation(b):
do stuff
EDIT: Sorry, but I have another question on nested lists. See this code:
def change(y):
yN = y[:]
for i in range(len(yN)):
if yN[i][0] == 1:
yN[i][0] = 0
else:
yN[i][0] = 1
return yN
data1 = [[1],[1],[0],[0]]
data2 = change(data1)
Here it doesn't work. Why? Again: how to avoid this problem? I understand why it is not working: yN = y[:] copies values of y to yN, but the values are also lists, so the operation would have to be doubled for every list in list. How to do this operation with nested lists?
Python variables contain pointers, or references, to objects. All values (even integers) are objects, and assignment changes the variable to point to a different object. It does not store a new value in the variable, it changes the variable to refer or point to a different object. For this reason many people say that Python doesn't have "variables," it has "names," and the = operation doesn't "assign a value to a variable," but rather "binds a name to an object."
In plusOne you are modifying (or "mutating") the contents of y but never change what y itself refers to. It stays pointing to the same list, the one you passed in to the function. The global variable y and the local variable y refer to the same list, so the changes are visible using either variable. Since you changed the contents of the object that was passed in, there is actually no reason to return y (in fact, returning None is what Python itself does for operations like this that modify a list "in place" -- values are returned by operations that create new objects rather than mutating existing ones).
In plusOne2 you are changing the local variable a to refer to a different integer object, 3. ("Binding the name a to the object 3.") The global variable a is not changed by this and continues to point to 2.
If you don't want to change a list passed in, make a copy of it and change that. Then your function should return the new list since it's one of those operations that creates a new object, and the new object will be lost if you don't return it. You can do this as the first line of the function: x = x[:] for example (as others have pointed out). Or, if it might be useful to have the function called either way, you can have the caller pass in x[:] if he wants a copy made.
Create a copy of the list. Using testList = inputList[:]. See the code
>>> def plusOne(y):
newY = y[:]
for x in range(len(newY)):
newY[x] += 1
return newY
>>> y = [1, 2, 3]
>>> print plusOne(y), y
[2, 3, 4] [1, 2, 3]
Or, you can create a new list in the function
>>> def plusOne(y):
newList = []
for elem in y:
newList.append(elem+1)
return newList
You can also use a comprehension as others have pointed out.
>>> def plusOne(y):
return [elem+1 for elem in y]
You can pass a copy of your list, using slice notation:
print plusOne(y[:]), y
Or the better way would be to create the copy of list in the function itself, so that the caller don't have to worry about the possible modification:
def plusOne(y):
y_copy = y[:]
and work on y_copy instead.
Or as pointed out by #abarnet in comments, you can modify the function to use list comprehension, which will create a new list altogether:
return [x + 1 for x in y]
Just create a new list with the values you want in it and return that instead.
def plus_one(sequence):
return [el + 1 for el in sequence]
As others have pointed out, you should use newlist = original[:] or newlist = list(original) to copy the list if you do not want to modify the original.
def plusOne(y):
y2 = list(y) # copy the list over, y2 = y[:] also works
for i, _ in enumerate(y2):
y2[i] += 1
return y2
However, you can acheive your desired output with a list comprehension
def plusOne(y):
return [i+1 for i in y]
This will iterate over the values in y and create a new list by adding one to each of them
To answer your edited question:
Copying nested data structures is called deep copying. To do this in Python, use deepcopy() within the copy module.
You can do that by make a function and call this function by map function ,
map function will call the add function and give it the value after that it will print the new value like that:
def add(x):
return x+x
print(list(map(add,[1,2,3])))
Or you can use (*range) function it is very easy to do that like that example :
print ([i+i for i in [*range (1,10)]])

Why do I have to 'return' a function? [duplicate]

This question already has answers here:
How do I get a result (output) from a function? How can I use the result later?
(4 answers)
Python difference between mutating and re-assigning a list ( _list = and _list[:] = )
(3 answers)
Closed 6 months ago.
I created a function and I want to append a number to the list:
def num(f):
list1.append(i)
return list1
list1 = []
i = 1
print "Now list1 is %s and i is %d" % (list1, i)
num(list1)
i += 1
print "Now list1 is %s and i is %d" % (list1, i)
num(list1)
i += 1
print "Now list1 is %s and i is %d" % (list1, i)
print list1
print i
Why do I have to return a function? It works with and without the return.
I was told that the function returns None if no return-statement was reached. But the function, mentioned above, works even if I don't type this return-statement.
I see you don't understand how functions work, so I added comments to your code to explain a little, but I suggest you to read Python tutorial about functions and wiki article further to gain understanding.
Also, I omitted many details not to overload the explanation. Important thing is there're immutable (i.e. integer, i in your example) and mutable (i.e. list, list1 in your example) types in Python and depending on this the behavior will be different.
def num(f):
#Here the argument you pass to the function is named 'f'
#and you don't use it
#The next line uses 'list1', that is defined in global scope
#since you didn't redefined this name inside the function
#Variable 'i' is also the one in global scope for same reasons
list1.append(i)
#Here you return 'list1', though you don't use this value
#further in your program. Indeed, you would not write a return
#statement the function would return 'None' as the return value
return list1
#Here you define 'list1' in global scope, and it will be used
#inside 'num' function, even without providing it as the argument
list1 = []
#Here you define 'i' in global scope, and it will be used
#inside 'num' function
i = 1
#Here you print 'i' and 'list' from global scope
print "Now list1 is %s and i is %d" % (list1, i)
#Here you call 'num' function and 'list1' provided as argument
#is assigned to 'f' inside the function, but you didn't used it and
#and instead used names from global scope - that's why it works in
#this way (however it is wrong use of function)
#With 'list1.append(i)' the 'list1' is modified in place so it
#doesn't matter if it is returned or not
num(list1)
#As to 'num' return value, it will be the same value as 'list1', but
#you don't use it here, to use it it needs to be assigned with '=' to
#some variable, i.e. 'list2=num(list1)', though in fact 'list1' and 'list2'
#will be the same all the time due to Python internals, but let's skip the
#details of this.
#You can see that the value returned is not 'None' - add the
#following line here:
print(num(list1))
#and run your program, the output will show you that it's a list returned.
#then remove the 'return' line in your function and run program again
#the output here will show, that is's 'None' that was returned.
So to fix the obvious mistake in the function:
def num(f):
f.append(i)
return f
but i is still used from global scope and not passed as argument, so even better:
def num(f_var,i_var):
f_var.append(i_var)
return f_var
Though the list will be modified inplace and you don't really have to return it
in you particular example, so:
def num(f_var,i_var):
f_var.append(i_var)
list1=[]
i=1
num(list1,i)
will work too.
In the provided example, returning a value is unnecessary because the append list method, and by extension your function as well, operates by side effect. Functions called only for side effects are not functions in the mathematical sense (mappings of domain values to codomain values), they serve to change state of mutable objects, in this case by appending an item to a list. While such functions can return a value, Python has a convention that functions invoked purely for side effect, such as list.append, only return None, denoting no useful return value.
If your function were side-effect-free, you would need to have a return statement for it to be useful at all. As an example, compare:
def add(a, b):
return a + b
...with the syntactically just as legal, but pretty useless:
def add(a, b):
a + b
# without a return statement, None is returned,
# and the calculated sum discarded
You don't use the return value, so it makes no difference, what you return. You also don't use the argument. You probably wanted to write
def append_num(f, i):
f.append(i)
and use two arguments:
append_num(list1, i)

List is still empty after function call

I'm new to python. I want to input an array of integer via the command line.
This works like a charm:
number = []
number = map(int,raw_input().split())
But if I put it into a function:
def data_entry(array_input):
array_input = map(int,raw_input().split())
if __name__=='__main__':
number = []
data_entry(number)
then print len(number) will always return 0.
What did I do wrong?
Instead, use a return statement in your function:
def data_entry():
return map(int,raw_input().split())
if __name__=='__main__':
number = data_entry()
print len(number)
Currently your function creates and modifies a local copy of number, the original stays unchanged.
Returning the new list is an option but there is a possibility to code what you originally wanted.
But first an important lesson!
The problem is the following:
array_input is just a name bound to the list object in this case number.
When you now pass it to the function the list will be bound to the name array_input in the scope of the function.
When writing array_input = map(int,raw_input().split()) you are binding the name array_input to a new list, because map(...) creates a new list.
If you want to modify your old list, you would have to copy the objects in the new list to the old one.
old code with comments:
def data_entry(array_input):
# map() creates a new list and array_input is bound to it
# the binding to your original list is lost!
array_input = map(int,raw_input().split())
New version with correct behaviour:
#test-input: 1 2 3
def data_entry(array_input):
#bind the list created by map to a new name
new_arr = map(int, raw_input().split())
#loop through the new list and append the objects to the original list
for obj in new_arr:
array_input.append(obj)
if __name__ == '__main__':
number = []
data_entry(number)
print(number)
>>[1, 2, 3]

Categories