538

In Python, when should you use lists and when tuples?

Sometimes you don't have a choice, for example if you have

"hello %s you are %s years old" % x

then x must be a tuple.

But if I am the one who designs the API and gets to choose the data types, then what are the guidelines?

codeforester
  • 39,467
  • 16
  • 112
  • 140
flybywire
  • 261,858
  • 191
  • 397
  • 503
  • 29
    Unfortunately this question has been wrongly marked as a duplicate. A big reason you will often see lists being used over tuples in situations with no strong reasons for either is readability. Round braces are used for many things in Python, but square brackets are used only for list-related things. E.g. when passing a list as an argument, it's just so much easier to spot that than when passing a tuple: `my_func([e1, e2])` vs `my_func((e1, e2))` – Hubert Grzeskowiak Jul 19 '18 at 18:56

7 Answers7

862

Tuples are fixed size in nature whereas lists are dynamic.
In other words, a tuple is immutable whereas a list is mutable.

  1. You can't add elements to a tuple. Tuples have no append or extend method.
  2. You can't remove elements from a tuple. Tuples have no remove or pop method.
  3. You can find elements in a tuple, since this doesn’t change the tuple.
  4. You can also use the in operator to check if an element exists in the tuple.

  • Tuples are faster than lists. If you're defining a constant set of values and all you're ever going to do with it is iterate through it, use a tuple instead of a list.

  • It makes your code safer if you “write-protect” data that does not need to be changed. Using a tuple instead of a list is like having an implied assert statement that this data is constant, and that special thought (and a specific function) is required to override that.

  • Some tuples can be used as dictionary keys (specifically, tuples that contain immutable values like strings, numbers, and other tuples). Lists can never be used as dictionary keys, because lists are mutable.

Source: Dive into Python 3

Shlomo
  • 120
  • 2
  • 8
jldupont
  • 93,734
  • 56
  • 203
  • 318
  • 63
    The "write-protect" analogy only goes so far: the membership of a tuple cannot be changed *but* mutable elements of a tuple *can* be changed: l = list(); t = (l, l); l.append(1) – Ned Deily Nov 10 '09 at 15:06
  • 13
    What makes you think tuples are faster then lists? – Winston Ewert Jan 01 '12 at 02:41
  • 3
    Tuples aren't faster than list. In [220]: t_range = tuple(range(100000)) In [221]: 99999 in t_range Out[221]: True In [222]: l_range = range(100000) In [223]: 99999 in l_range Out[223]: True In [224]: %%timeit .....: 99999 in l_range .....: 100 loops, best of 3: 2.97 ms per loop In [225]: %%timeit .....: 99999 in t_range .....: 100 loops, best of 3: 3.01 ms per loop – Kracekumar Nov 01 '13 at 16:29
  • 34
    @kracekumar: That's because in your example you first created a list and then made it a tuple. Obviously it is slower. Look at this: `$ python -m timeit "for x in xrange(10000):" " ''.join( ['a','b','c','d','e','f','g'] )" 1000 loops, best of 3: 1.91 msec per loop $ python -m timeit "for x in xrange(10000):" " ''.join( ('a','b','c','d','e','f','g') )" 1000 loops, best of 3: 1.17 msec per loop` – LeartS Mar 28 '14 at 15:37
  • Can I call it a method of defining "constant list"? I got the answer here: http://jtauber.com/blog/2006/04/15/python_tuples_are_not_just_constant_lists/ – Jithin Pavithran Oct 28 '16 at 17:49
  • 8
    Since the edit was rejected and there is no way to add this as an answer, here's a comment to add more info: Copying or cloning a tuple is not as straightforward as a list or dictionary. For e.g., you can clone a list using `list2 = list1[:]`. Similarly you can clone a dictionary as well using `dict2 = dict1.copy()` but tuples themselves don't have that method. You can use `deepcopy` method from the `copy` module if you need to do that. – Vishnu Narang Jan 23 '17 at 06:03
  • 15
    @VishnuNarang Except you can clone a tuple just fine with `tuple2 = tuple1[:]`? Although there is no point to doing so since the resulting object is both a) immutable and b) identical to the first one. The only time it makes sense is when the tuple has mutable children, but then you have to make a deepcopy regardless of whether it's a dict, list, or tuple. – Wlerin Mar 25 '18 at 21:05
  • @jldupont It appears to me that iterating over a list is faster than iterating over a tuple: my_tuple = (range(100)) my_list = [range(100)] timeit "for item in my_tuple: None" results in 2.59 us, while timeit "for item in my_list: None" results in 118 ns. Am I missing something with this test or is your statement not entirely accurate? – Dan Boschen Mar 13 '19 at 12:59
  • @jldupont Ok I To update I found my error in comment above; ((range(100)) is still a range object, not a tuple. Repeating the above with tuple(range(100)) vs list(range(100)) results is reasonably identical results; even when I replace None with some operation on "item". So at this point I am convinced there is no speed difference between each if you are "iterating through a constant set of values". If you are looping through to build a container, lists clearly rule on the speed metric over tuples and would be the reason for having the mutable container in the first place (speed vs safety) – Dan Boschen Mar 13 '19 at 19:20
  • 1
    @LeartS: Not a super-useful benchmark; a `tuple` of constants is cached, a `list` of constants isn't (compare output of `dis.dis("''.join( ('a','b','c','d','e','f','g') )")` to `dis.dis("''.join(['a','b','c','d','e','f','g'])")`); most of the work here is the `join` itself. Most real `tuple`s aren't made entirely of compile-time constant literals. If you use variables (defined in setup) named `a` through `g` (with the same values), then do `''.join((a,b,c,d,e,f,g))`/`''.join([a,b,c,d,e,f,g])`, `tuple` will still win, but by amounts so small it's almost indistinguishable from rounding error. – ShadowRanger Feb 26 '20 at 02:44
  • `tuple`s win is largely from: 1) Reduced memory fragmentation (`list`'s header and the underlying array are allocated separately, `tuple`'s is contiguously allocated as a single block) and 2) `tuple` uses free lists for small `tuple`s so it often avoids going back to the memory allocator when it wants to build a new `tuple` (it just grabs one off the dedicated `tuple` free list). Neither of these are huge, but they'd add up if `tuple` wasn't an option. – ShadowRanger Feb 26 '20 at 02:46
  • @WinstonEwert: Why tuples are faster: See https://stackoverflow.com/a/3340881/1783801 where it gets measured and explained – Jaleks Aug 06 '20 at 14:56
  • You can use the `in` keyword for lists as well. – scrollout Jul 18 '21 at 22:02
268

There's a strong culture of tuples being for heterogeneous collections, similar to what you'd use structs for in C, and lists being for homogeneous collections, similar to what you'd use arrays for. But I've never quite squared this with the mutability issue mentioned in the other answers. Mutability has teeth to it (you actually can't change a tuple), while homogeneity is not enforced, and so seems to be a much less interesting distinction.

Supamee
  • 615
  • 5
  • 15
Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
  • 4
    @Ned so, say you wanted to implement a Point class. Would you use a tuple or a list to hold x, y, z coordinates? You would want to change the values (go with list), but at the same time order and position is meaningful and consistent (go with tuple?). – Arlen Aug 23 '11 at 15:13
  • 60
    You're missing the semantics of it by comparing to the wrong feature of `struct`. Tuples are useful when _position has relevance_ - the best example comes from coordinates in mathematics, which even uses the same syntax: `(x, y, z)` – Izkata Apr 18 '13 at 19:08
  • 2
    Mutability aside, why don't we always use dicts instead of tuples? This would give the advantage of naming the fields, which would make accessing individual elements much more readable... – GreenAsJade Feb 19 '15 at 05:48
  • 7
    Arlen: Mutable points are actually a really bad idea. Stick with immutable points and create new ones, they're cheap enough. If you are dealing with millions of points, use the Flyweight pattern and keep a cache of recently used points. – John Cowan Mar 31 '15 at 13:37
  • So, think about a read-only lock state of an array. If you really need to edit a tuple, you don't, you create an array from it. As @Izkata points, coors, and some other data can be static. Example, a process that starts with 10 elements, do not need to edit. Just save a tuple started, then create you array the way you want. You'll be able to access both data by just pointing the tuple or array. This may sound confusing, but is not. – m3nda Jun 12 '15 at 16:06
  • 13
    @GreenAsJade: That's what `collections.namedtuple` is for (and unlike `dict`, the only cost is to define the `namedtuple`; the instances are exactly the same size in memory as an equally long `tuple`, but can be accessed by `.name` as well as index. The classic use cases for `tuple` as a heterogeneous lightweight "object" could all be replaced with `collections.namedtuple`, but it's unnecessary in many cases (a loop over `zip` can just unpack to named variables, so the tuples aren't accessible by name, but you never use the `tuple`s directly in the first place). – ShadowRanger Oct 21 '16 at 20:15
  • 2
    @JohnCowan - it isn't that they are a bad idea - it is that they don't makes sense. Points are logically immutable just like integers - you don't assign a new value to 5 by adding 1 to it - you have a new integer 6. The same is true of a point because it is just two integers. If I change its values I have not altered the points identity - I have a different point with a different identity. Mutable objects change their value without changing their identity but an immutable object's value is its identity. – Fraser Feb 16 '18 at 11:44
  • They are great for vectors and other stuff that can be classified as being just data, but such structures tend to attract behaviors. At that point, one might as well define a proper data type instead. – MauganRa Apr 24 '18 at 11:19
  • 2
    @Bachsau Micro-optimization is the wrong reason to choose a data structure. And you should be nicer :) – Ned Batchelder Nov 17 '18 at 11:50
  • @NedBatchelder Only if there aren't any better reasons, and perceived 'conventions' do not count as such. The reason why lists are more often used for homogenous objects is not convention, but that they are mutable, which means they can easily be created in loops or used to put things in order, while tuples are often used to group things that belong together. It does not mean, that there's anything wrong with putting objects of different types into lists or create tuples of homogenous objects, when you have them available at the point where the tuple is created. Just use what suites your needs – Bachsau Dec 14 '18 at 02:33
89

I believe (and I am hardly well-versed in Python) that the main difference is that a tuple is immutable (it can't be changed in place after assignment) and a list is mutable (you can append, change, subtract, etc).

So, I tend to make my tuples things that shouldn't change after assignment and my lists things that can.

thornomad
  • 6,707
  • 10
  • 53
  • 78
53

Must it be mutable? Use a list. Must it not be mutable? Use a tuple.

Otherwise, it's a question of choice.

For collections of heterogeneous objects (like a address broken into name, street, city, state and zip) I prefer to use a tuple. They can always be easily promoted to named tuples.

Likewise, if the collection is going to be iterated over, I prefer a list. If it's just a container to hold multiple objects as one, I prefer a tuple.

AFoglia
  • 7,968
  • 3
  • 35
  • 51
  • 3
    This is the only good answer here. It states the truth, not just mutable/immutable stuff which everyone knows. – Oleh Prypin Sep 25 '12 at 16:44
  • 6
    Why would you use a tuple, and even a named tuple, instead of a dict? – GreenAsJade Feb 19 '15 at 05:50
  • 3
    Some justification for the preferences would be nice. Why do you prefer tuples for heterogenous objects? Why do you prefer a list if you want to iterate? – bugmenot123 Sep 01 '15 at 11:14
  • 3
    @GreenAsJade, I know this is old but the reason is, tuple still is an iterable, and order matters (dicts, order isn't guaranteed). – triunenature Sep 10 '17 at 05:03
  • "Why do you prefer tuples for heterogenous objects? Why do you prefer a list if you want to iterate?" Because if I only care about iteration, I don't usually care about the length of the list. Add an item; remove one; the code doesn't care. The code invoked during iteration is independent of the number of items, so there's no reason to force it to be fixed by object type. It's not a strong reason, but kind of a good smell. You might have lists where they are ordered, but if you have lists where each element is different, it's a lazy way to avoid defining a real class. – AFoglia Sep 11 '17 at 04:54
  • 1
    Lists have class methods such as sort() that are useful only on homogeneous types. If you stick a number and a string in a list, you break sort(). Besides that there is the mutability, obviously. – Louic Jun 05 '18 at 11:01
  • There is no reason to use a list that is only intended to be iterated over **without change**! Such a case is excellent for a tuple – Shlomo Jan 28 '23 at 18:27
19

The first thing you need to decide is whether the data structure needs to be mutable or not. As has been mentioned, lists are mutable, tuples are not. This also means that tuples can be used for dictionary keys, wheres lists cannot.

In my experience, tuples are generally used where order and position is meaningful and consistant. For example, in creating a data structure for a choose your own adventure game, I chose to use tuples instead of lists because the position in the tuple was meaningful. Here is one example from that data structure:

pages = {'foyer': {'text' : "some text", 
          'choices' : [('open the door', 'rainbow'),
                     ('go left into the kitchen', 'bottomless pit'),
                     ('stay put','foyer2')]},}

The first position in the tuple is the choice displayed to the user when they play the game and the second position is the key of the page that choice goes to and this is consistent for all pages.

Tuples are also more memory efficient than lists, though I'm not sure when that benefit becomes apparent.

Also check out the chapters on lists and tuples in Think Python.

Amanda Nyren
  • 495
  • 2
  • 6
  • 1
    What makes you think tuples are more memory efficient then lists? – Winston Ewert Jan 01 '12 at 02:42
  • 5
    +1 - while mutability/immutability is an important consideration, position having relevance is _the_ primary reason - mathematics even uses the same syntax for coordinate systems: `(x, y, z)` – Izkata Apr 18 '13 at 19:10
  • Why don't you use dicts instead of tuples, which have named entries? It's not clear to me why the position is significant in your case: one is the action the other is the result ... how should I (the reader) know in advance that this is the order of these things? In your case it's debatable, but I see wider tuples used a lot where the access of them becomes `next_thing = result_tuple[5]`. 5? Really? Why 5? Wouldn't it be better to say `next_thing = result['next_item'] `? – GreenAsJade Feb 19 '15 at 05:47
  • Well tuples are hashable, and thats why they can be used as dictionary keys. Has nothing to do with immutability. – RoadRunner Mar 31 '20 at 09:29
11

But if I am the one who designs the API and gets to choose the data types, then what are the guidelines?

For input parameters it's best to accept the most generic interface that does what you need. It is seldom just a tuple or list - more often it's sequence, sliceable or even iterable. Python's duck typing usually gets it for free, unless you explicitly check input types. Don't do that unless absolutely unavoidable.

For the data that you produce (output parameters) just return what's most convenient for you, e.g. return whatever datatype you keep or whatever your helper function returns.

One thing to keep in mind is to avoid returning a list (or any other mutable) that's part of your state, e.g.

class ThingsKeeper
    def __init__(self):
        self.__things = []

    def things(self):
        return self.__things  #outside objects can now modify your state

    def safer(self):
        return self.__things[:]  #it's copy-on-write, shouldn't hurt performance
Rafał Dowgird
  • 43,216
  • 11
  • 77
  • 90
  • So if you have a function, and it has a dreaded default method parameter (say, a list), and you return that parameter, code outside can now mess with the list? – Rob Grant Feb 19 '15 at 09:56
  • @RobertGrant Correct! One more reason to use the "None as the default with the if in the function prologue" pattern. It might be more verbose but avoids nasty surprises like this one. – Rafał Dowgird Feb 19 '15 at 11:13
  • Yeah, that makes perfect sense (and I would say I think that eliminates the need for the default parameters to behave the way they do, but then I get yelled at :)). Thanks. – Rob Grant Feb 20 '15 at 13:13
4

A minor but notable advantage of a list over a tuple is that lists tend to be slightly more portable. Standard tools are less likely to support tuples. JSON, for example, does not have a tuple type. YAML does, but its syntax is ugly compared to its list syntax, which is quite nice.

In those cases, you may wish to use a tuple internally then convert to list as part of an export process. Alternately, you might want to use lists everywhere for consistency.

Doug F
  • 350
  • 2
  • 10
  • 6
    The standard json encoder converts tuples to arrays. – Nelo Mitranim Aug 02 '15 at 19:12
  • Thanks for pointing that out. My comment was meant generally, though. Personally, I export to yaml as often as json and in those cases would have to manually convert tuples to lists. – Doug F Aug 20 '15 at 19:13
  • 1
    @NeloMitranim Yes, but when you encode a tuple to json, then decode it later, you end up with a list instead of a tuple. – agemO Jul 26 '21 at 06:26
  • Fair point. I agree with the parent comment, using lists avoids this gotcha entirely. – Nelo Mitranim Jul 26 '21 at 10:06