Python, change value of the arguments within the function - python

I'm trying to change the value of the list that i put as argument in the function.
this is the code:
def shuffle(xs,n=1):
if xs: #if list isn't empty
if n>0:
#gets the index of the middle of the list
sizel=len(xs)
midindex=int((sizel-1)/2)
for times in range(n):
xs=interleave(xs[0:midindex],xs[midindex:sizel])
return None
The interleave code returns a list with the values of both lists mixed up.
However when i run:
t=[1,2,3,4,5,6,7]
shuffle(t,n=2)
print t
The list t didn't changed it's order. The function needs to return None so i can jst use t=shuffle(t,n). There's anyway i can do this?

Your problem is right here:
xs=interleave(xs[0:midindex],xs[midindex:sizel])
You're making slices of the list to pass to your interleave() function. These are essentially copies of part of the list. There's no way that what comes back from the function can be anything than a different list from xs.
Fortunately, you can just reassign the new list you get back into the original list. That is, keep xs pointing to the same list, but replace all the items in it with what you get back from the interleave() function.
xs[:]=interleave(xs[0:midindex],xs[midindex:sizel])
This is called a slice assignment. Since xs remains the same list that was passed in, all references to the list outside the function will also see the changes.

xs is a reference local to the function, and is independant of t. When you reassign xs, t still points to the original list.
Since you must not return anything from the function, a workaround is to keep a reference to the original list and repopulate it using slice assignment:
orig_xs = xs
# do stuff here
orig_xs[:] = xs

Related

Why would a list() method be used on a python list in a recursion problem?

I'm currently learning Depth-First Search in Python and a problem asked that, given a Binary Search Tree and a number N, find all paths from root-to-leaf such that the sum of all the node values of each path equals N.
I did everything right, but my code didn't work (it resulted in an empty 2D array). When looking at the solution, the only difference was "allPath.append(list(currPath))", while the code I wrote was simply 'allPath.append(currPath)'. When I made this change, the code worked perfectly Here's the full code:
def findPathSum(root, sum):
allPath = []
_findPathSum(root, sum, [], allPath)
return allPath
def _findPathSum(currNode, sum, currPath, allPath):
if currNode is None:
return
currPath.append(currNode.val)
if currNode.val == sum and currNode.left is None and currNode.right is None:
print(currPath)
allPath.append(list(currPath))
else:
_findPathSum(currNode.left, sum-currNode.val, currPath, allPath)
_findPathSum(currNode.right, sum-currNode.val, currPath, allPath)
del currPath[-1]
What I'm confused about is that currPath is already a list, and only contains integers (which are the node values). When I print currPath before it is appended to allPaths, it also correctly displays a list with integer values. Yet after I append it to allPaths, allPaths is just an empty array. However, using the list() method on it, for some reason, displays the correct 2D array with the right integer values. I have no clue why this would work.
From my understanding, the list() method simply takes an iterable and turns it into a list...however currPath was already a list. I feel like I'm missing something really obvious.
list creates a brand new list (although the elements are not brand new), a new list object. In your case, without using list you will simply be appending the exact same list object to allPath on each recursive call.
Therefore, since all the elements of allPath are the exact same list, changing that list changes all of the elements of allPath. For example, when at the end of _findPathSum you do del currPath[-1], you are effectively deleting the final element of every element of allPath. Since in the end currPath will be empty, that is what you see at the end in allPath - a list containing empty lists.

Python List Append not working on sliced lists

I have a list a = [1,2,3,4,5]
I don't understand why the following code doesn't produce [2,3,4,5,1]
a[1:].append(a[0])
I tried reading up on the append() method as well as list slicing in Python, but found no satisfactory response.
a[1:] gives you a whole new list, but you're not assigning it to any variable so it it just thrown away after that line. You should assign it to something (say, b) and then append to it (otherwise append would change the list but return nothing):
a = [1,2,3,4,5]
b = a[1:]
b.append(a[0])
And now b is your desired output [2,3,4,5,1]
I think here you just not really understand append function. Just like my answer in Passing a variable from one file into another as a class variable after inline modification, append is an in-place operation which just update the original list and return None.
Your chain call a[1:].append(a[0]) will return the last call return value in the chain, so return append function value, which is None.
Just like #flakes comment in another answer, a[1:] + a[:1] will return your target value. Also you can try a[1:][1:][1:] which will return some
result. So the key point is the append function is in-place function.
See python more on list
You might have noticed that methods like insert, remove or sort that only modify the list have no return value printed – they return the default None. 1 This is a design principle for all mutable data structures in Python.
First, append the first element a[0] to the list
a.append(a[0])
and then exclude the first element
a[1:]

Append select dict keys to a list [duplicate]

I am trying to append objects to the end of a list repeatedly, like so:
list1 = []
n = 3
for i in range(0, n):
list1 = list1.append([i])
But I get an error like: AttributeError: 'NoneType' object has no attribute 'append'. Is this because list1 starts off as an empty list? How do I fix this error?
This question is specifically about how to fix the problem and append to the list correctly. In the original code, the reported error occurs when using a loop because .append returns None the first time. For why None is returned (the underlying design decision), see Why do these list operations return None, rather than the resulting list?.
If you have an IndexError from trying to assign to an index just past the end of a list - that doesn't work; you need the .append method instead. For more information, see Why does this iterative list-growing code give IndexError: list assignment index out of range? How can I repeatedly add elements to a list?.
If you want to append the same value multiple times, see Python: Append item to list N times.
append actually changes the list. Also, it takes an item, not a list. Hence, all you need is
for i in range(n):
list1.append(i)
(By the way, note that you can use range(n), in this case.)
I assume your actual use is more complicated, but you may be able to use a list comprehension, which is more pythonic for this:
list1 = [i for i in range(n)]
Or, in this case, in Python 2.x range(n) in fact creates the list that you want already, although in Python 3.x, you need list(range(n)).
You don't need the assignment operator. append returns None.
append returns None, so at the second iteration you are calling method append of NoneType. Just remove the assignment:
for i in range(0, n):
list1.append([i])
Mikola has the right answer but a little more explanation. It will run the first time, but because append returns None, after the first iteration of the for loop, your assignment will cause list1 to equal None and therefore the error is thrown on the second iteration.
I personally prefer the + operator than append:
for i in range(0, n):
list1 += [[i]]
But this is creating a new list every time, so might not be the best if performance is critical.
Note that you also can use insert in order to put number into the required position within list:
initList = [1,2,3,4,5]
initList.insert(2, 10) # insert(pos, val) => initList = [1,2,10,3,4,5]
And also note that in python you can always get a list length using method len()
Like Mikola said, append() returns a void, so every iteration you're setting list1 to a nonetype because append is returning a nonetype. On the next iteration, list1 is null so you're trying to call the append method of a null. Nulls don't have methods, hence your error.
use my_list.append(...)
and do not use and other list to append as list are mutable.

Preserving the current value of a local variable when there is recursion

I'm writing a function to flatten a nested array (Python list). e.g turn [1,2,[3]] into [1,2,3], [[1,2,[3]],4] into [1,2,3,4] etc.
I have the following:
def flatten_array(array):
flattened_array = []
for item in array:
if not isinstance(item, list):
flattened_array.append(item)
else:
flatten_array(item)
return flattened_array
So the idea is to have the function be recursive, to handle situations where there is nesting to an unknown depth. My problem is that flattened_array is getting re-initialized each time a nested list is encountered (when flatten_array is called recursively).
print flatten_array([1,2,[3]])
[1,2]
How can I maintain the state of flattened_array when recursive calls are made?
Change the lines
else:
flatten_array(item)
to
else:
flattened_array+=flatten_array(item)
So the full function reads like
def flatten_array(array):
flattened_array = []
for item in array:
if not isinstance(item, list):
flattened_array.append(item)
else:
flattened_array+=flatten_array(item)
return flattened_array
this gives
flatten_array([1,2,[3]]) # [1,2,3]
flatten_array([1,2,[3,[4,5]]]) # [1,2,3,4,5]
flatten_array([1,2,[3,[4,5]],6,7,[8]]) # [1,2,3,4,5,6,7,8]
Your original code is not doing anything with the recursive call. You get back the result on the list, but just discard it. What we want to do is attach it to the end of the existing list.
Additionally, if you don't want to keep creating temporary arrays, we can create one array with the first call to the function and just append to it.†
def flatten_array(array,flattened_array=None):
if flattened_array is None:
flattened_array = []
for item in array:
if not isinstance(item,list):
flattened_array.append(item)
else:
flatten_array(item,flattened_array)
return flattened_array
The results of this version are the same, and it can be used the same way, but in the original, each call to the function creates a new empty array to work with. Normally this isn't a problem, but depending on the depth or how large the sub-arrays are this can build up in memory.
This version flattens the array into a given array. When called with just the input (like flatten_array([1,2,[3]])), it creates an empty array to work with, otherwise it just adds to the given array (thus the recursive call just needs to give the array to add to), modifying it in place.
This has the advantage of allowing you to add to an existing array if we want:
a = [1,2,3]
b = [2,3,[4]] # we want to add flatten this to the end of a
flatten_array(b,a) # we don't bother catching the return result here
print(a) # [1,2,3,2,3,4]
† There is a subtle point here. You may ask why we didn't define the function as def flatten_array(array,flattened_array=[]) and get arid of the test inside the function. Try that and call the function a few times. What happens is that the default value is created once at function definition and not each time the function is called. This means that the default array which is modified in place is shared by each function call, resulting in it accumulating the results.
This is likely not what we want. By setting the default value to None and creating a new empty array inside the function each time, we ensure that each call to the function has a unique empty array to work with.

Python Function Not Working

I am trying to create a function, new_function, that takes a number as an argument.
This function will manipulate values in a list based on what number I pass as an argument. Within this function, I will place another function, new_sum, that is responsible for manipulating values inside the list.
For example, if I pass 4 into new_function, I need new_function to run new_sum on each of the first four elements. The corresponding value will change, and I need to create four new lists.
example:
listone=[1,2,3,4,5]
def new_function(value):
for i in range(0,value):
new_list=listone[:]
variable=new_sum(i)
new_list[i]=variable
return new_list
# running new_function(4) should return four new lists
# [(new value for index zero, based on new_sum),2,3,4,5]
# [1,(new value for index one, based on new_sum),3,4,5]
# [1,2,(new value for index two, based on new_sum),4,5]
# [1,2,3,(new value for index three, based on new_sum),5]
My problem is that i keep on getting one giant list. What am I doing wrong?
Fix the indentation of return statement:
listone=[1,2,3,4,5]
def new_function(value):
for i in range(0,value):
new_list=listone[:]
variable=new_sum(i)
new_list[i]=variable
return new_list
The problem with return new_list is that once you return, the function is done.
You can make things more complicated by accumulating the results and returning them all at the end:
listone=[1,2,3,4,5]
def new_function(value):
new_lists = []
for i in range(0,value):
new_list=listone[:]
variable=new_sum(i)
new_list[i]=variable
new_lists.append(new_list)
return new_lists
However, this is exactly what generators are for: If you yield instead of return, that gives the caller one value, and then resumes when he asks for the next value. So:
listone=[1,2,3,4,5]
def new_function(value):
for i in range(0,value):
new_list=listone[:]
variable=new_sum(i)
new_list[i]=variable
yield new_list
The difference is that the first version gives the caller a list of four lists, while the second gives the caller an iterator of four lists. Often, you don't care about the difference—and, in fact, an iterator may be better for responsiveness, memory, or performance reasons.*
If you do care, it often makes more sense to just make a list out of the iterator at the point you need it. In other words, use the second version of the function, then just writes:
new_lists = list(new_function(4))
By the way, you can simplify this by not trying to mutate new_list in-place, and instead just change the values while copying. For example:
def new_function(value):
for i in range(value):
yield listone[:i] + [new_sum(i)] + listone[i+1:]
* Responsiveness is improved because you get the first result as soon as it's ready, instead of only after they're all ready. Memory use is improved because you don't need to keep all of the lists in memory at once, just one at a time. Performance may be improved because interleaving the work can result in better cache behavior and pipelining.

Categories

Resources