James,
I went back and re-read the plain text message and this is what I saw:
The idea is to modify random.shuffle to shuffle dictionary’s key/value pairs even if the keys don’t happen to be a range of integers.
My impression at that time was that key/value pairs would remain pairs but that asking to display the dictionary would show those pairs in some randomized order. Further, asking to view all keys and/or values would be shown n the new order and removing a value would maintain the order and popping an item, ditto.
Perhaps I misread it but I have trouble imagining it as a reasonable request for making random.shuffle also shuffle dictionaries analogously to how it shuffles lists. This would in no way be analogous in my mind as rearranging a list containing inner lists is normally done by preserving the inner lists albeit in a new order.
And, I was not paying total attention to the OP and their request but entered the conversation a bit later.
I am looking at the code in the rest of the message now. Indeed, for numeric keys it is doing a shuffle of values while leaving keys in the same order. I am not willing to consider this an expected behavior.
So, I looked for the source code for the function:
>>> import inspect
>>> import random
>>> print(inspect.getsource(random.shuffle))
def shuffle(self, x):
"""Shuffle list x in place, and return None."""
randbelow = self._randbelow
for i in reversed(range(1, len(x))):
# pick an element in x[:i+1] with which to exchange x[i]
j = randbelow(i + 1)
x[i], x[j] = x[j], x[i]
The comment suggests it is expecting a list. I do not see it taking a dict and converting it but it does look like it just happens to be able to use the hooks inside a dict to address keys that are numeric as in a[2]
happens to match saying give me the value stored in the key of 2
. That is almost a coincidence and it does indeed scramble the values leaving the keys in place.
But since the code uses an i
and a j
that are numeric indexes, of course, this cannot be expected to work for dictionaries containing almost anything else than the kind of sample that happened to work and I continue to suggest this is not what I would want as a result. I would expect items to be swapped, not just values.
Looking further at the code he uses, yes, this is indeed a result he wants, albeit one just as easily done using other tools and then making it into a dictionary using methods suggested including especially the one I thought was wrong when it seems I was looking at the wrong problem.
To make his simple non-Zorro alphabetic cypher is fairly straightforward with something like this that makes the dict at the end:
import string
import random
alf1 = list(string.ascii_lowercase[:26])
alf2 = list(string.ascii_lowercase[:26])
random.shuffle(alf2)
coder = dict(zip(alf1, alf2))
The contents of coder are:
>>> coder
{'a': 'd', 'b': 'k', 'c': 'h', 'd': 'g', 'e': 'b', 'f': 'm', 'g': 'u', 'h': 'i', 'i': 'l', 'j': 't', 'k': 'e', 'l': 'f', 'm': 'n', 'n': 'a', 'o': 'y', 'p': 'p', 'q': 's', 'r': 'z', 's': 'q', 't': 'x', 'u': 'w', 'v': 'o', 'w': 'c', 'x': 'r', 'y': 'v', 'z': 'j'}
And should have the keys in alphabetical order followed by scrambled values.
If this was the expected result, and you wanted to extend random.shuffle() so it checks the class of what it is working on, it likely could replace the internal dictionary using a variant of the above (or something way better) as my alf1 would be the keys and alf2 would be the initial values.
But this entire discussion now seems to rest on a coincidence that perhaps made the OP ask why that fluke was not working everywhere. The real problem is that it should not have worked at all if the code insisted on refusing to work on dictionaries and returned some kind of error signal.
Perhaps a better request would be to extend a class like dict, or some subclass, to support one or more methods that could be called on a dict. One would perhaps scramble just the values. Another might scramble intact key/value pairs.
Objects are mainly supposed to be a way to encapsulate being able to do something to themselves, rather than having external functions be able to handle almost any object handed to them. That may have exceptions and some of that handling is done delicately by adjusting dunder methods to help the other function.
I apologize, again, if my misunderstanding led us down other paths. I could delete or edit messages, if that was appropriate, but then some replies may not seem quite right.
I also note a subtlety. Objects often have a choice on how and when they actually do something. As an example, if I add things to some implementation of a dict, could it choose to buffer the additions until it had say 5 or 10 by perhaps having a secondary storage area such as a list or dict and only periodically merge the results in? As long as it handled the searches and other needs, would it matter?
I can envision many other such things that may be done for speed or other considerations that are all okay as long as the external interfaces look the same. As an example, the old implementation of an ordered dictionary relied on a secondary way to index the keys and you can imagine the current implementation of dict being lazy about re-ordering in a way similar to this. Some implementations may never have to re-order unless asked to display the results.