How does local scope work?

I have a question.
this is the code I used:

def remove_middle(lst,start,end):
rlist=lst[:start]+lst[(end+1):]
return rlist

If I didn’t create the new list “rlist” and just used “return lst…etc…” as someone showed above, wouldn’t it have just modified the original list “lst”? Was that the goal, to modified the existing list?

… I keep thinking they want new lists…haha!:upside_down_face:

If you assign the inputted lst to any new variable name, even lst, a copy will be made, and that new variable name will remain local to your function. Your original list will not be changed.
For a function to change a list in place, the list elements must be changed.

EDIT: The above paragraph stands corrected by rtf, in his comments below. If you change the elements of the copy of a list, inside or outside of the function, the original list will certainly be changed. blogrunner’s list (above) doesn’t change because no elements of the original list were changed; a new list was simply created from some of them.

Example: Write a function which replaces the middle element of a list with the list [‘abc’] if the length of the list is odd, and inserts the list [‘abc’] between the two middle elements if the length is even.

def replace_middle(lst):
    if len(lst) % 2 != 0:
        lst[len(lst)//2] = ['abc']
    else:
        lst.insert(len(lst)//2, ['abc'])
   
lst_1 = [4, 8, 15, 16]
lst_2 = [4, 8, 15, 31, 16]

replace_middle(lst_1)
replace_middle(lst_2)

print(lst_1)
print(lst_2)  

# Output:
[4, 8, ['abc'], 15, 16]
[4, 8, ['abc'], 31, 16]

Notice first, that there is no return statement, and, second, that within the function, list methods were used that do not return a new list, but which operate on the list itself.

1 Like

May I take it that this is what your question is referring to?

def remove_middle(lst, start, end):
    return lst[:start] + lst[end:]

This will have no effect on the original list but is a new list constructed from the original. We can print it or assign it to a variable at the caller.

s = list('abcdefghijklmnopqrstuvwxyz')
list_without_middle = remove_middle(s, 7, 17)
print (list_without_middle)
# ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
print (s)
# ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']

It is important to note that when we pass a list object to a function, even though it be given a different local name, it is not a copy, but the global list, itself we are acting upon, Any change to the list in the function will be reflected in the global object.

Above we do not manipulate or modify the list in any way. We merely extracted the data points we wanted and returned them as a new object.

1 Like

yes, that was the post I was referring to.
I am more confused. In your example with s and list_without_middle you created a variable and stored the modified list (which is pretty much what I did in my exercise). however, in the example above that, there is no variable assignment…just the line with return. This is where I interpreted it to mean that the argument was overwritten with the new values.

Is it because return stores whatever value comes from the function, thereby “acting as a variable” and why there isn’t a local variable in the function?
I wonder if I am creating redundant variables…or maybe I am getting mixed up with tuples…see I know just enough to get myself in trouble…

1 Like

return is part of the assignment back to the caller. It doesn’t store anything, just hands it back. The caller can then either print it (so return is assigning the argument for print()) or it can assign it to a variable (so return is assigning the value that variable will take on).

def foo(x):
    return x * 2

bar = foo(21)

print (bar)            #  42

print (foo(84 / 4))    #  42

We can, as one sees compose the return value directly in the statement rather than instancing a variable and returning its value. The variable does not get returned, only polled for its value which is sent up the pipeline to the caller.

Maybe so, but that won’t harm anything. Work with what you understand and replace when you are ready to advance in your thinking. My tendency is to compose the return value in place. It’s only a choice, not a better way. Either way is fine.

Just to be clear…

s is a global object. We pass it to the function along with the other two positional arguments expected by the function.

list_without_middle = remove_middle(s, 7, 17)

The variable above is also a global. It takes the value returned by the function. It is not part of the function, proper, only the returned value.

1 Like