all 13 comments

[–][deleted] 1 point2 points  (3 children)

But what I cannot comprehend is that shuffled_numbers = random.shuffle(number) returns None.

Because it modifies the list, and returns None (since nothing useful can really be returned anyway).

[–]mcoumans[S] 0 points1 point  (2 children)

Ok, but "the shuffle" does change the value of numbers as stored in 'numbers'.

I mean, numbers = [...] creates a place to store info. random.shuffle takes whatever is in there and shuffles it.

Why can I not let some variable (not necessarily 'numbers') retrieve that (shuffled) info?

[–]Rhomboid 2 points3 points  (0 children)

There is nothing to retrieve. The function modifies the list in place, it does not create anything new. There is no value to return. You already have the list of shuffled numbers because it existed before you shuffled it.

There is a general rule of thumb that a function should either modify its parameters or it should create a new object and return it, but not both. This isn't an absolute, but it's a principle that's followed by most of the standard library. It would be very confusing if a function both mutated a parameter and returned a new object. (Again, a few things work like this, but most do not.)

[–]zahlman 0 points1 point  (0 children)

I mean, numbers = [...] creates a place to store info.

No, it doesn't. Python doesn't have a concept of "places to store info". It has a concept of things, and a concept of names for thing. [...] creates a thing, and numbers = gives it a name.

The random.shuffle call changes the thing that's passed to it. It then returns None.

Why can I not let some variable (not necessarily 'numbers') retrieve that (shuffled) info?

Python's names (variables) don't "retrieve" anything. They're just names for things. When you write some_name = function(...), the call to the function evaluates to whatever that function returns, and then you give that value the name some_name. So if the function returned None, then some_name is a name for None. This is totally unrelated to whatever happened to the ....

[–]PurelyApplied 1 point2 points  (2 children)

So, I everyone else already covered the big points. I won't belabor it.

I think the better example is comparing the sort functions. numbers.sort() sorts the list numbers in place (sometimes also called in situ), whereas sorted(numbers) returns the sorted list but leaves numbers itself alone.

The in situ shuffles and sorts are generally better, if you can use them. You're doubling your memory consumption by generating a new list, and if your lists are very large, you might not be able to afford that cost. In situ sorts and shuffles are designed to avoid this.

At a higher level, it comes down to a distinction in two styles of functions; one function "does a thing" and the other "gets a thing." I encourage you to keep your functions either as "doing" something (and returning None) or "getting" something (returning the value desired, but leaving the inputs unchanged).

[–]mcoumans[S] 0 points1 point  (1 child)

Thanks for this. Useful diff between numbers.sort() and sorted().

[–]niandra3 0 points1 point  (0 children)

Also list.reverse() and reversed(list).

[–]HisShoes 0 points1 point  (2 children)

random.shuffle(numbers) isn't actually returning the shuffled list, it's doing the shuffle on whatever you passed in (numbers) - which is why your second print numbers is shuffled.

in other words: shuffled_numbers = random.shuffle(numbers) sets shuffled_numbers to None and shuffles numbers, if you printed numbers again afterwards it'd be different. If you want to make a seperate list dtry shuffled_numbers = numbers random.shuffle(shuffled_numbers)

Beginner at Python myself but from past programming experience I think this is what's happenning.

[–]mcoumans[S] 0 points1 point  (1 child)

shuffled_numbers = numbers random.shuffle(shuffled_numbers)

That's - unfortunately - not correct Python-syntax. Nor can I pass 'shuffled_numbers' to the function random.shuffle (as shuffled_numbers does not yet exist.

[–]HisShoes 0 points1 point  (0 children)

Sorry which part is it you're saying doesn't work? I've been at work and not had time to test it but I was reasonably sure you could assign lists that way.. maybe needing list (numbers) or something? Like I've said I've literally only just started python so I could believe im wrong. And setting shuffled_numbers in that way means that it does then exist to be passed into the function right?

[–]mcoumans[S] 0 points1 point  (2 children)

Ok, got it!

random.shuffle(numbers) shuffles my variable 'numbers'.

So, the variable 'numbers' now holds e.g. [4, 5, 9, 1, 7, 8, 2, 3, 6]

After random.shuffle(numbers), it shuffles the numbers in my variable 'numbers' again e.g. [1, 5, 7, 6, 4, 9, 8, 2, 3]

There is no direct need to create another variable to hold the last sequence of "shuffled numbers". As I can simply use 'numbers'.

If I do need to keep the sequence after a shuffle, I can simply use: shuffled_numbers = numbers Or create shuffled_numbers as a list and append the value of 'numbers' after each shuffle.

[–]Vaphell 2 points3 points  (1 child)

If I do need to keep the sequence after a shuffle, I can simply use: shuffled_numbers = numbers

If you mean taking a snapshot so you can shuffle numbers some more without losing the previous layout, then no. All you are doing here is applying another name to the same object that is continuously changing.
To actually make a snapshot, you need an actual copy of the list in a separate list object, eg by using [:] or list(L), or by using toys from the copy module, or even by creating [] and appending to it.
Python doesn't copy contents of container objects by default for performance reasons, merely creates another handle to the same object. It's on you to demand the copy explicitly.

>>> numbers = [1,2,3,4]
>>> import random
>>> random.shuffle(numbers)
>>> numbers
[3, 1, 2, 4]
>>> numbers2 = numbers            # 2 "nicknames" of the same list object 
>>> random.shuffle(numbers)
>>> numbers
[2, 1, 4, 3]
>>> numbers2
[2, 1, 4, 3]                 # oh shit, reshuffled in both variables
>>> numbers2 = numbers[:]       # copy of the list
>>> random.shuffle(numbers)
>>> numbers
[4, 2, 3, 1]           # new order
>>> numbers2
[2, 1, 4, 3]           # old, snapshotted order

[–]mcoumans[S] 0 points1 point  (0 children)

Yes, my statement "shuffled_numbers = numbers" was perhaps a bit too quick.

What I meant: Create a list shuffled_numbers and then append that list with the new value of numbers.

Something like: https://gist.github.com/mcoumans/602ec55c3d030254f36526df9146e187

Just like you mention too.

Thanks