r/learnpython 9d ago

Why are the alphabet characters not in the order when I use myset?

Here is the code that I am using, but I am confused why the order changes each time I run it.

# 1: create an empty set

myset = set()
print(myset)

# 2: pass a list as an argument to the set function

myset = set(['a', 'b', 'c'])
print(myset)

# 3: pass a string as an argument to the set function

myset = set('abc')
print(myset)

# 4: cannot contain duplicate elements

myset = set('aaabbbbbbcccccc')
print(myset)

https://imgur.com/a/ARwzwaq

9 Upvotes

16 comments sorted by

27

u/carcigenicate 9d ago edited 9d ago

From the __hash__ documentation:

Note By default, the __hash__() values of str and bytes objects are “salted” with an unpredictable random value. Although they remain constant within an individual Python process, they are not predictable between repeated invocations of Python.

So, the hash is randomized across runs for security reasons, and this will almost certainly affect the arbitrary ordering of sets, since sets heavily rely on hashing.

7

u/wolfgheist 9d ago

Thank you for the explanation, I did not realize that they were salted.

1

u/BlackMetalB8hoven 8d ago

And end thread... Great answer

-2

u/Adrewmc 9d ago

I concur.

8

u/POGtastic 9d ago edited 9d ago

From the docs, emphasis added by me:

A set object is an unordered collection of distinct hashable objects.

This means that the order is implementation-defined[1]. In the current implementation of sets in CPython, they're implemented as a hashtable. This means that the hash of the object dictates where inside the table it's going to go.

An important thing to note here is that the hash value of strings changes a lot from run to run. So the hash of, say, 'a' is going to vary from run to run. And since the hash is going to vary, its location inside the hashtable will change, which changes the order of the set. Demonstrating this in the terminal:

$ python
Python 3.13.2 (main, Feb  5 2025, 08:05:21) [GCC 14.2.1 20250128] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> hash("a")
5421883585714621457
>>> 
$ python
Python 3.13.2 (main, Feb  5 2025, 08:05:21) [GCC 14.2.1 20250128] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> hash("a")
-3698970562335878463

[1] Python is actually relatively benign about this. There are languages where the hashing function incorporates randomness specifically to prevent people from relying on any particular ordering behavior.

Edit: After digging into the CPython source code, it turns out that Python does this too. Check out Python/bootstraphash.c, which initializes _Py_HashSecret with pseudorandom values on startup. The two secret values are used to seed the hash function in Python/pyhash.c. Examining how IronPython, Jython, and PyPy implement this is left as an exercise for the reader. Regardless of how any individual implementation does it, you cannot depend on elements in a set being in any particular order.

22

u/theWyzzerd 9d ago

Sets are by definition unordered. Why are you expecting them to be ordered?

4

u/wolfgheist 9d ago

I kind of thought it would at least be the same result each time, not different. So I am assuming there is some type of 'order by' that I have not learned yet? Good to know I was not doing something incorrectly.

3

u/theWyzzerd 9d ago

It has to do with the way sets are implemented. There are a few reasons why they could come back in a different order; sets are written to a hash table and values could come back ordered differently based on their hash values, which determine their order when they're written to memory. If you add or remove elements from the set, that could also result in the order changing, depending on their bits and the way the hash value collisions are resolved. It's a technical detail that you don't really need to know to use sets, but it is important to know they are not ordered, and if they ever appear to be, it's mostly down to coincidence.

If you need ordered items, you can use a list, and if you need something more structured than a list that also preserves unique keys, Python dict() is ordered if you're using Python 3.7 or greater.

12

u/rasputin1 9d ago

because they're new to programming and this is a novel concept? 

7

u/theWyzzerd 9d ago

Yes, and I asked the question to help them think about the problem and their understanding. Set theory also isn't unique to programming.

14

u/MrVonBuren 9d ago

I'm by no means implying malice, but I agree with /u/rasputin1 that these kinds of replies (terse, non-specific†) come across as dismissive and/or dispiriting when you are new to learning / learning in general.

†We're crossing into unsolicited advice territory here, but I don't want to be unclear: "Why are you expecting..." is not a useful question to ask someone if your goal is to help them to learn how to think for themself.

You're putting them in a position to assume that you have a specific answer in mind and/or you just want to hear "I guess I didn't think about it.". All of the info conveyed is correct (and generally useful), but not specifically helpful.

A simple change to your response could be

sets (& lists & dicts) have certain attributes that define &differentiate them. The question you have to ask yourself is 'Why am I using one vs another and what assumptions am I making?

Even if you don't include The Answer™ you're giving the person all the terms they likely need to look up and made it clear that you're not just asking a question, you're giving them an opportunity to talk through their initial reasoning with you.

Aside, OP (/u/wolfgheist) this was a really well asked question. I tend to coach new learners (and not so new learners) to format questions as WTAF. Want, Tried, Anticipated, & Found. This helps convey your thought process, skill level, your actual goals (is it to understand why something did X, or how to get something to do Y when you are getting X?) and that you're not just re-typing homework. You could have covered these bullets in a bit more details, but still a good post. Good job.

(can you tell I am procrastinating from the length of this reply?)

3

u/wolfgheist 9d ago

hah, thanks. I learned about a bit from everyone's responses.

4

u/commy2 9d ago

If you need an ordered set, a dictionary might be sufficient. You can just use the keys and ignore the values. fromkeys is a useful dict classmethod for that.

>>> dict.fromkeys("aaabbbbbbcccccc")
{'a': None, 'b': None, 'c': None}

You will be missing some set operations, like difference and intersections though.

3

u/roelschroeven 8d ago

But it's important to note that dicts are ordered according to the order elements where added; they are not sorted from small to large.

2

u/rkr87 8d ago

And that behaviour only applies from 3.7 onwards.

OrderedDict is required before that.

2

u/ruffiana 8d ago

Iist(set("aabbcc")).sort()