3

I have the following code:

def radixSort(A):

    #get max amount of digits

    A = sortByDigit(A, maxDigits) #this works
    print(A) #prints A as sorted

if __name__ == "__main__":
    A = [int(100*random.random()) for i in range(10)]
    radixSort(A)
    print(A) #prints unsorted

Why does changing A in radixSort not change A in the main method ? I realize I could simply add a return statement in radixSort, and an assignment statement in the main method, but the code has to pass the following test case:

    def testrRadixSort(self):
        A = [4, 3, 2]
        radixSort(A)
        self.assertEqual(A, [4,3,2])

5 Answers5

4

sortByDigit isn't sorting inplace. It's creating a new list and returning a reference to that.

You can replace the contents of A with the content of the new list with this simple change

A[:] = sortByDigit(A, maxDigits) #this works

Alternatively you could modify sortByDigit so it does sort inplace

John La Rooy
  • 295,403
  • 53
  • 369
  • 502
1

The problem is on this line:

A = sortByDigit(A, maxDigits)

You created a local assignment of A, which does not overwrite the global copy of A.

You need to make sortByDigit modify the contents of A, not return a new list.

An alternative is to add

global A

Before that line so it will modify the global A.

However with regards to global variables see kindall's comment below.

korylprince
  • 2,969
  • 1
  • 18
  • 27
  • I didn't downvote, but I'll guess it's because you suggest making A a global variable. – Cody Piersall Oct 28 '13 at 01:49
  • It wasn't a suggestion, it was more of an explanation. But for one-off scripts I see no reason not to use global variables. – korylprince Oct 28 '13 at 01:54
  • I have a deep aversion, possibly even irrationally so, for globals. But like I said, I didn't downvote you. Heck, I'll even upvote you, since you're the only one that mentioned the alternative. – Cody Piersall Oct 28 '13 at 01:59
  • 2
    I'm the one who downvoted you. The fact that the variable representing the list being sorted happens to be named the same inside and outside the function is coincidental and recommending the use of a global variable wrecks the function for general use. It's not just bad practice, it will lead to the function not working properly if it's called from, say, another function. – kindall Oct 28 '13 at 02:22
  • @CodyPiersall - so do I. But they are useful at times (I didn't mean to sound cross at you if I did.) – korylprince Oct 28 '13 at 02:34
  • @kindall, good point. I only added the global part as a last minute edit because it would fix the specific problem he was having. I didn't think through it fully. My bad. – korylprince Oct 28 '13 at 02:36
0

This behaviour is about passing arguments by reference or by value. There already is a great explanation of this behaviour on StackOverflow.

This means: You cannot reassign the given list, but you can modify the existing instance, e. g. with the append method.

Community
  • 1
  • 1
Robin Krahl
  • 5,268
  • 19
  • 32
  • So basically when I pass a pointer as parameter, it creates a separate pointer that points to the same value as the parameter ? – DDFirst Name Dd Oct 28 '13 at 01:59
  • The Python mantra is: all parameters are passed by value, but all values are references to objects. :-) – kindall Oct 28 '13 at 02:40
0

I'm assuming sortByDigit() returns a sorted copy of the list rather than sorting it in place. In which case, simply replace the contents of the list with the result of that call, via slice assignment:

A[:] = sortByDigit(A, maxDigits)
John La Rooy
  • 295,403
  • 53
  • 369
  • 502
kindall
  • 178,883
  • 35
  • 278
  • 309
0

Nah, you can't change A like that. Keep in mind that A in your radixSort function (local scope) is different with A in your main function (global scope).

To achieve what you need, you can declare it as a global variable. However, this is not the best method since using global vars can get you to a confusing scope problem, therefore it's not recommended. But this how to do it:

def radixSort(lst): #we can refer to the passed variable as lst
    global A

This doesn't sort A inplace, but it assigns the sorted lst to A.

Or better, assign the sorted value to A with slice notation:

A[:] = sortByDigit(A, maxDigits) #store the sorted list in A

Or even better, return it as sorted, and then reassign A with the sorted value:

def radixSort(A):

    #get max amount of digits

    A = sortByDigit(A, maxDigits) #this works
    print(A) #prints A as sorted
    return A

And in your main program:

if __name__ == "__main__":
    A = [int(100*random.random()) for i in range(10)]
    A = radixSort(A)
    print(A) #prints sorted
    self.assertEqual(A, [4,3,2]) #self.assertEqual(sorted, unsorted)

Also, it's not a good practice to capitalize identifiers. Capitalized words is usually reserved for classes.

So:

def radixSort(a):

    #get max amount of digits

    a = sortByDigit(a, maxDigits) #this works
    print(a) #prints A as sorted
    return a

if __name__ == "__main__":
    a = [int(100*random.random()) for i in range(10)]
    a = radixSort(a)
    print(a) #prints sorted
    self.assertEqual(A, [4,3,2]) #self.assertEqual(sorted, unsorted)

Hope this helps!

aIKid
  • 26,968
  • 4
  • 39
  • 65
  • 1
    I like this answer the best because it advocates *returning* the answer instead passing it in/out of the routine as an argument. The former is more idiomatic python (I think the term pythonic is tossed around too much, but if there were a time to use it it would be here). – SethMMorton Oct 28 '13 at 02:20
  • Returning the sorted list raises the question: is this a copy, or did you sort it in place? The caller needs to know. Sorting in-place is generally preferable because less memory is needed; in that case you should return `None` (as Python does for similar operations). – kindall Oct 28 '13 at 02:24
  • The question of whether it's better to sort in place vs. returning the sorted list is independent of whether sorting in-place is possible. This answer is wrong because it incorrectly gives the impression that sorting in-place is not possible, and I'm not a fan of opening by mentioning `global` either, especially given `list`s are mutable. (EDIT: I also see kindall raised similar objections on korylprince's answer) – Free Monica Cellio Oct 28 '13 at 05:07
  • @alKid I just looked again and I don't see a single in-place sort in your post, nor even a mention of the possibility. Unless you count the solution with `global` which I don't. This is a basic example of an in-place sort: https://gist.github.com/ChadAMiller/7197157 – Free Monica Cellio Oct 28 '13 at 13:56
  • oh, I just looked again and saw the slice thing. I guess that would be close to an in-place sort, though the explanation is wrong; it doesn't look for a global variable. If you change A to L within the function but leave it A in the main program, it will still do the same thing. – Free Monica Cellio Oct 28 '13 at 14:14
  • Hm.. yes i see. I just realized that i didn't provide a correct explanation. Thanks for mentioning that. – aIKid Oct 28 '13 at 14:15
  • Your welcome! One other thing, I just tested with an interpreter and (in 2.7 and 3.3 at least) the first block will actually throw an error. Python rightfully complains if you try to have a global with the same name as a parameter since only one can exist in scope anyway. The fix would be to remove the parameter entirely (which demonstrates just how bad an idea the `global` solution is) – Free Monica Cellio Oct 28 '13 at 14:18
  • Another alternative is to use a different name.. But this will get troublesome if you're trying to sort another list. To be honest, i hate globals as well. – aIKid Oct 28 '13 at 14:28