List is still empty after function call - python

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]

Related

Python Function: UnboundLocalError: local variable 'tuple' referenced before assignment

The first block of code works (lines 15 - 21).
The error is occurring in the second block (lines 24 - 30).
Here is my code:
# converting numerical input into list and tuple <-- line 15
data = input("provide numbers separated by ',': ")
list = data.split(",")
tuple = tuple(list)
print("list:", list, "tuple:", tuple)
def convert(): # <-- line 24
data = input("provide numbers separated by ',': ")
list = data.split(',')
tuple = tuple(list)
print("list:", list, "tuple:", tuple)
convert()
I have read solutions that suggest declaring the variable in the global namespace, but the solutions do not seem to be working for my function. And a solution still eludes me.
The function has the same code as the first block, its just wrapped in a function. I don't understand why the code works at the top level, but not inside a function.
Does anyone know what's happening here?
If you re-bind a non-global variable name (such as a = b) anywhere within a function(1), it is made a local variable for that entire function (even before the modification).
That means that tuple = tuple(list) is assigning to a local tuple, and your use of tuple(list) is using that variable, rather than the actual built-in tuple() function. Since that variable name is not bound at this point, you get the "use before set" error.
This is why it's a bad idea to use built-in function names as variable names, which is something you appear to have done with abandon, both tuple and list :-)
So I would suggest two things:
rename your variables to prevent clashes (use my_list and my_tuple for example).
make my_tuple global within the function (better would be to avoid globals altogether).
Avoiding globals can be as simple as:
def convert():
data = input("provide numbers separated by ',': ")
my_list = data.split(",")
my_tuple = tuple(my_list)
print("list:", my_list, "tuple:", my_tuple)
return my_tuple
outer_tuple = convert()
(1) This does not include simply modifying the variable's value such as changing one item in a list:
x = [1, 2, 3]
x[1] = 42 # Mutation of x, not re-binding.
x = [7, 8, 9] # Re-binding x to new object.
And note that for immutable types (like int for example), x = 7 is re-binding x to a different object 7, not changing the value of the object "behind" x. This particular aspect of Python (the fact that all things are objects and variable names are simply bound to those objects) was one of my greatest epiphanies when learning the language.

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)

Return list from while loop in a function?

I'm trying to return a list my_list created within function make_list to use in the function print_elems.
I keep getting the error
my_list is not defined
for when I ask to print it, after calling "make_list".
What am I doing incorrectly in trying to return "my_list"?
def make_list():
my_list = []
print("Enter \"-999\" to return list.")
x = int(input("Enter a number: "))
while x != -999:
my_list.append(x)
x = int(input("Enter a number: "))
return my_list
def print_elems(user_list):
print(user_list, sep=' ')
make_list()
print(my_list)
print_elems(my_list)
You are trying to access the local variable my_list. You have to use the returned value instead by assigning it to a variable:
some_name = make_list() # assign function result to variable
print(some_name)
print_elems(some_name)
On a side note, you probably want to slightly modify print_elems:
def print_elems(user_list):
print(*user_list, sep=' ')
The * unpacks the list and passes its elements to the print function. Otherwise, when passing a single positional argument to print, the sep parameter will never be used.
You need to assign the return of your function to a variable:
tata = make_list()
print(tata)
The variable my_list is destroyed when you leave the scope of your function that defined it. That is why you return it.
See Short Description of the Scoping Rules? and PyTut: Scopes and namespaces

How do I read a list that is located within another class?

In Python I have a for loop which calls a class, which in turn calls another class and so on, with classes manipulating data, performing sql inserts etc. The final class contains a list of all the files which have been created. I want to access this list from outside the class but I cannot work out how to!
(I know there is also a loop issue-will explain more below!)
A basic example is:
#A class to create the list
class Create_list():
def list(self,j):
l=j+1
#pass this variable to another class, get_list
Get_list().input(l)
#class get_list receives the number from create_list and appends it to mylist
class Get_list():
def input(self,l):
mylist=[]
mylist.append(l)
#print mylist
# loop through a list of numbers and feed them into the create_list class
j=10
for k in range(j):
Create_list().list(k)
#I want to access the list here. I have tried all of the below
readlist=Get_list().input().mylist # with ()
readlist=Get_list.input.mylist # without ()
x=Create_list() # create an object with class name
mylist=x.list().mylist #use above object
I have tried all the approaches in the last block of code.
I can't use the first two as the function list requires an input, which comes from the preceding class. (the error says that list() requires two arguments, only one is provided (self I assume)).
I have tried assigning the class to an object but this too does not work.
I realise that the for loop means that if I were to print mylist within def input there is only the value from that value of j.
I basically would like to access mylist, which has a list of values (l) from all of the values in j after that for loop has run.
Lots of stuff is wrong here so I'll just show a simple way to do it:
class Create_list(object):
def __init__(self):
self.list = []
def input_list(self, x):
l = x + 1
self.list.append(l)
j=10
cl = Create_list()
for k in xrange(j):
cl.input_list(k)
print cl.list
Another possibility is to return the list:
def add_one_to(j):
l=j+1
return(l)
def store(mylist, l):
mylist.append(l)
return(mylist)
Usage:
>>> mylist = []
>>> myintegerplusone = add_one_to(1)
>>> store(mylist, myintegerplusone)
>>> print(mylist)
[2]
In that case you could imagine a function as a craftsman, you give him something to fix/manipulate and he then returns the fixed/manipulated good back to you.
I think what you want is to store inside the class object the list using the "self" method and then access it from outside.
Try this code:
class CreateList():
def __init__(self):
self.list = []
if __name__ == "__main__":
c = CreateList()
c.list.append(4)
print c.list

calling to function in function

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.

Categories