Bonus fact: You can use set iteration in for loops as well! This has the added benefit of sorting the values as well:
>>> for x in {2, 1, 3}: ... print(x) 1 2 3
You didn't ask for that, but I felt like sharing it, to there you go
I don't think you can rely on this? Happy to be proven wrong, but set does not guarantee iteration order. There may be a distinction if all elements are available at set construction, but that seems like a fiddly rule I would rather avoid.
>>> [i for i in {9,40,49}] [40, 9, 49]
Also, you can make anything iterable in python if you implement __iter__.
I learned this when a unit test that iterated over a set started failing sporadically :P super confusing bug!
This is because the string hash function is randomized on process startup to help prevent DOS attacks: https://stackoverflow.com/q/30585108/1517969
>>> for x in {2, 1, 3}: print(x)
1
2
3
>>> for x in {20, 10, 30}: print(x)
10
20
30
>>> for x in {200, 100, 300}: print(x)
200
100
300
0: https://stackoverflow.com/questions/15171695/whats-with-the-...For example you can see that by adding a large power of two to the numbers you can see that they are sorted by the remainder (as it seems that the default number of buckets is a power of two):
> for x in {2048 + 1, 256 + 3, 1024 + 2, 512 + 4}: print(x)
2049
1026
259
516whereas each time you run id() on a number outside that range you will get a different id as a new object is instantiated
>>> for x in 1, 2, 3: ...
But that's just wrong. Tuples are not just immutable lists. They're for heterogeneous collections and are meant to be destructured or otherwise have their elements acted on individually. Lists are for homogeneous collections and they're meant to be looped over. Immutability is neither here nor there. The performance benefit is negligible and irrelevant.
If you don't believe me, look at how type hints work: an arbitrary-length list of integers is typed like `list[int]`, but you have to write an arbitrary-length tuple of integers like `tuple[int, ...]`. It looks different -- why? Because the way you're meant to use tuples is like `tuple[int, int, str, datetime]`, where there's a fixed number of elements and each one has a distinct meaning, where it usually doesn't make sense to loop over and treat them the same.
It's also why there's list comprehension but no tuple comprehension.
The suggestion in the example is just plain incorrect.
I've never read this opinion before, so I don't think it was intended from the get-go or became a commonly adopted standard. I can see some sense to it, though.
But lists aren't just for looping. They often get looped over, but that's true for any collection, even dictionaries.
>It's also why there's list comprehension but no tuple comprehension.
That's because (x for x in y) was chosen for making generators. Just stick tuple in front and you've got a tuple comprehension.
https://docs.python.org/3/library/stdtypes.html
>Tuples are immutable sequences, typically used to store collections of heterogeneous data (such as the 2-tuples produced by the enumerate() built-in). Tuples are also used for cases where an immutable sequence of homogeneous data is needed (such as allowing storage in a set or dict instance).
GvR email from 2003:
https://mail.python.org/pipermail/python-dev/2003-March/0339...
>Tuples are for heterogeneous data, list are for homogeneous data. >Tuples are not read-only lists.
Immutable sequence is a use-case, but it's a secondary use-case. The primary use-case is for record-type data.
>But lists aren't just for looping. They often get looped over, but that's true for any collection, even dictionaries.
My point was more that tuples are not primarily suited for looping over, not that lists exclusively are.
>That's because (x for x in y) was chosen for making generators. Just stick tuple in front and you've got a tuple comprehension.
But list comprehensions were in the language for several years before generators came around. There was a time when [x for x in foo] worked but (x for x in foo) was a syntax error. Why didn't they make it a tuple at the time? Because that's not what tuples are for.
If you look at everywhere tuples show up in the standard library, it's almost always in some context where looping over the collection is not a primary consideration. The clearest example of this dichotomy is in the string methods: the str.split method gives you a list of strings, because the result can be of arbitrary length and you're likely going to loop over it. But str.partition gives a tuple because there are always exactly 3 elements in the result and you're meant to destructure it.
They aren’t just immutable lists logically, but since Python (while it can use a static typechecker for verifying correctness to some extent) doesn’t use type information about what is stored in containers for storage implementation, effectively at runtime they are just immutable lists.
But, even logically, and apart from that implementation consideration, immutable lists are one of the things they are, are intended to be, ans are designed to be used for.
> They're for heterogeneous collections and are meant to be destructured or otherwise have their elements acted on individually.
That is another use case, yes. The existence of that use case does not negate the immutable list use case.
> It looks different -- why?
tuple type hints have a form (that you point out) for homogenous arbitrary length immutable lists as well as one for fixed-length potentially-heterogenous ordered collections because tuples are intended to serve both purposes, not just the latter.
> It's also why there's list comprehension but no tuple comprehension.
No, genexps using () and having broader utility is why there are no tuple comprehensions. Heck, if genexps were around first, we might not have list, set, or dictionary comprehensions, either.
if x in {1, 2, 3}:
print(x)There used to be a difference in python2 -- the list option would turn into n LOAD_CONSTs and a BUILD_LIST, whereas the tuple option would just emit one LOAD_CONST.
import dis
def with_tuple():
for i in (1,2,3):
print(i)
def with_list():
for i in [1,2,3]:
print(i)
print("with_tuple")
dis.dis(with_tuple)
print("with_list")
dis.dis(with_list)
edit: looks like this changed in python 3.6: https://python.godbolt.org/z/rEfY9GT9WMy understanding is that tuples are always more efficient and preferred unless mutability is needed.