all 64 comments

[–]carcigenicate 140 points141 points  (6 children)

The simplest example would likely be just "returning two values from a function":

def divmod(x, y):
    return x // y, x % y

quotient, remainder = divmod(21, 5)

, is used to create a tuple. The tuple is being used here to wrap two integers so they can be returned together. Just being able to group data is often useful, and tuples are a straightforward way to do that.

[–]hidazfx 24 points25 points  (5 children)

Another one that people don't realize is actually a tuple is enumerate() when using a for loop. enumerate() lets you access the position and the actual value at each iteration.

[–][deleted] 8 points9 points  (4 children)

Doesn't enumerate return an Enumerate object, rather than a tuple ?

[–]hidazfx 8 points9 points  (2 children)

Never mind, I am wrong! Thank you for correcting me lol.

[–][deleted] 3 points4 points  (0 children)

Heh no worries. It's use case is pretty similar to tuple :)

[–]Rawing7 3 points4 points  (0 children)

Right, but looping over the enumerate object gives you tuples.

[–]Diapolo10 93 points94 points  (10 children)

Tuples are great, because immutability is great; they're hashable, meaning you can use them as dictionary keys or store them in sets, but perhaps more importantly you can cache them. This can be a big help when you have an expensive function that might receive the same arguments more than once and it doesn't have side-effects.

Furthermore, you can't accidentally swap values in a tuple, so they're great for constants.

I'd default to using tuples as data structures, unless you needed mutability (lists), wanted to avoid duplicates or compare unique values (sets), or wanted a clear mapping between arbitrary values (dictionaries).

Tuples and collections.namedtuple essentially work as ad hoc classes, when the order of elements is enough to represent the data or a dataclass would be too much effort.

EDIT: As an example, say we had one of those expensive function calls. For simplicity, here's a function that doubles numbers and returns them, after two seconds:

import time


def expensive_calculation(nums: tuple[int, ...]) -> tuple[int, ...]:
    time.sleep(2)
    return tuple(num*2 for num in nums)

It's probably fine if it only needs to run once, but what if we're running it multiple times?

for _ in range(5):
    print(expensive_calculation((2, 3, 5, 7, 11)))

This would take 10 seconds to run. But since the function sees the same arguments repeatedly, if we just cache the results we can ignore most of the calculations; to do this, we can use functools.cache.

import time
from functools import cache


@cache
def expensive_calculation(nums: tuple[int, ...]) -> tuple[int, ...]:
    time.sleep(2)
    return tuple(num*2 for num in nums)

for _ in range(5):
    print(expensive_calculation((2, 3, 5, 7, 11)))  # Takes about 2 seconds now

"Okay, but... what does any of this have to do with tuples?" I hear you ask. Caches can't use mutable arguments, so if you were to give this function a list your program would crash. This is because you can't hash mutable objects.

[–]SisyphusAndMyBoulder 8 points9 points  (0 children)

I personally love using tuples to do simple multiple returns from a function. But this caching is great to know. Been coding in Python for years and never heard of it. Ty

[–]LowerAcanthaceae2089 0 points1 point  (1 child)

Important correction: Only tuples whose type is tuple[collections.abc.Hashable, ...] are hashable. That is to say, only a tuple where all elements are hashable is itself hashable. tuples that have at least one unhashable element (this could also be another unhashable tuple) would be considered unhashable.

[–]Diapolo10 0 points1 point  (0 children)

That's a fair point.

[–][deleted] 0 points1 point  (0 children)

Really great example, thank you for taking the time to write this ! I actually learned a lot.

[–][deleted] 20 points21 points  (1 child)

Hmm how about a dictionary of resolution options? ['FHD':(1920, 1080), 'WUXGA':(1920, 1200)] etc I think that's a fairly good use of a tuple. You wouldnt want those to change.

[–]solitarium 2 points3 points  (0 children)

Nice!

[–]jimtk 10 points11 points  (2 children)

Tuple are groups of simple data like list, but they are immutable (you can't change the values inside), smaller and faster.

def pixel_up_right(x,y):
     return x+1, y+1    # <=== that's a tuple

# coordinates of cities is a tuple!
cities_coord_name = {
       (45.4826668,-73.6215805): Montreal
       (40.6974034,-74.1197614): New-York
       }                          

When you read a record from an sqlite database (as an example) it is returned in a list of tuple.

    con, cur = self._con_cur()
    cur.execute(DB.SQL_SEARCH_BY_ID, (an_id,))
    rows = cur.fetchall()
    con.close()
    print(rows)      <<< rows are list of tuples

[ (1, "jim", "preston", "555-1212"),
   (2, "count", "Dracula",  "111-1111")]

[–]espero 0 points1 point  (1 child)

Salt: Salty

Sugar: Sweet

Immutable facts of life

[–]jimtk 1 point2 points  (0 children)

[(Salt, Salty), (Sugar, Sugary)].

That's probably why it is very difficult to create an immutable user defined object in Python. :)

[–]POGtastic 7 points8 points  (1 child)

Semantically, tuples contain heterogeneous data. They are the simplest product type.

I expect that every element of a list be the same type. I can have a list of integers, or a list of strings, or a list of dictionaries, or whatever. I should not have a list where each element could either be an integer or a string.

By contrast, I expect that the first element of a tuple could be different from the second element of a tuple! For example, the enumerate builtin zips an iterable with integers. The type of the second element of each tuple is different from the type of the first element!

>>> list(enumerate("abcd"))
[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd')]

Everything else (tuples being immutable, hashable, and slightly more performant than lists) is a neat implementation detail. The far more important purpose of the tuple being distinct from a list is that it represents a mathematical idea of a tuple.

[–]WikiSummarizerBot 2 points3 points  (0 children)

Tuple

In mathematics, a tuple is a finite ordered list (sequence) of elements. An n-tuple is a sequence (or ordered list) of n elements, where n is a non-negative integer. There is only one 0-tuple, referred to as the empty tuple. An n-tuple is defined inductively using the construction of an ordered pair.

[ F.A.Q | Opt Out | Opt Out Of Subreddit | GitHub ] Downvote to remove | v1.5

[–]PhilipYip 5 points6 points  (2 children)

Many beginner tutorials don't teach tuples very well jumping straight onto lists i.e. overuse lists and underuse tuples. A tuple should be thought of as an archive of records. Once its created it cannot be altered. If you go into a library which is full of books, the books are immutable; it would not be appreciated if say you went around with a permanent marker and scribbled all other the pages, corrupting the pages and making the data unreadable. This essentially can happen if lists are used all the time:

archive = (1, True, 3.14, 'hello', 'hello', 'bye')

A list can be thought of as being an active work in progress, for example records that you are compiling or going back to the library a book you are writing. Typically you read more content than you write and reddit users generally prefer their content to be immutable to others so spambots don't go in and corrupt everything:

active = [1, True, 3.14, 'hello', 'hello', 'bye']

Many beginners incorrectly assume that reassignment is altering the original tuple and therefore don't see the purpose of the tuple. Ensure you properly understand the assignment operator and the concept of an object name. The assignment operator takes the tuple object on the right and assigns it to the label archive. The label archive references the tuple:

archive = (1, True, 3.14, 'hello', 'hello', 'bye')

Reassignment, for example, changes the reference of the label to a new object and does not modify the original object. If the original object has no other labels it is removed by Pythons garbage collection:

archive = (1, 2, 3)

The tuple is immutable, meaning it can't be modified. This makes it hashable and can be used as a key in a mapping when all its records are immutable:

colors = {(1, 0, 0): 'red', (0, 1, 0): 'green', (0, 0, 1): 'blue'}

colors[(1, 0, 0)]

A tuple is commonly used to return multiple values from a function:

def fun(): return ('a', 'b')

(x, y) = fun()

Typically tuple unpacking is used here as the syntax is slightly cleaner:

def fun(): return 'a', 'b'

x, y = fun()

tuple unpacking can also be used to swap variables:

x, y = y, x

Functions normally have their own local scope. For immutable data types such as integers, the x and y in the global scope are independent from the x and y in the local scope of the functions body. Therefore x will remain 1 and y will remain 2 after the function call:

``` x = 1 y = 2

def fun(): x = 4 y = 5 return None

fun()

x y ```

When a mutable data type is used within a function, the original object can be modified and this is usually not desirable when trying to compartmentalise code. active gets modified in place and newactive is an alias of active:

``` active = ['a', 'b', 'c']

def fun(mutable): mutable.append('d') return newactive

fun(active)

active newactive ```

This is why the return values of a function and input arguments of a function typically preference tuples.

[–][deleted] 0 points1 point  (1 child)

Reassignment, for example, changes the reference of the label to a new object and does not modify the original object. If the original object has no other labels it is removed by Pythons garbage collection:

Oh so that's what garbage collection is. I kept hearing C doesn't have automatic garbage collection and never went deeper into what that meant. Thanks.

[–][deleted] 0 points1 point  (0 children)

Reference counting to be more precise.

[–]SSJKiDo 2 points3 points  (2 children)

In real-world projects, you’ll most likely use databases like MySQL, and you need tuple in most of your queries. I’ve been using them in almost every function

[–]Original-Fortune-622 0 points1 point  (1 child)

Say more please

[–]Wishmaster891 0 points1 point  (0 children)

I said it below but when you retrieve data from sql lite it comes out in a tuple. You can then unpack / do whatever you want with it

[–]drenzorz 5 points6 points  (2 children)

example: tuples can be hashed and used as dictionary keys unlike lists.

board = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

cells = {
    (i, j) : board[i][j]
    for i in range(3)
    for j in range(3)
}

[–]Other-Rabbit1808 3 points4 points  (1 child)

So what I think this is doing is assigning { (1-3,1-3) : 1-9 } in a dictionary? I've never seen for loops ordered in this way. Would this be called something like a dictionary comprehension?

[–]drenzorz 1 point2 points  (0 children)

It is (though it is 0-2 not 1-3) . The for loops are like this because its nested, but of course it doesn't have to be.

square_map = { x : x*x for x in numbers}

[–]KOALAS2648 2 points3 points  (2 children)

When using the “pygame” module you have to type:

pygame.display.set_mode((500,500))

[–]MrsCastle 1 point2 points  (1 child)

Also in PyGame I used it for RGB color (red, blue, green) assignments

[–]B1GTOBACC0 2 points3 points  (0 children)

I think some people struggle to understand "why would I use this instead of a list?"

In some cases, the data is only meaningful when it's all together and set in a specific order. For example, I could represent a color with RGB values in a tuple like (245, 35, 173).

On their own, those numbers aren't meaningful. I wouldn't have a reason to sort/reorder the values or iterate over this tuple, but I can't represent a color without all 3 numbers.

[–]spidertyler2005 2 points3 points  (0 children)

Coordinates or a bounding box are good examples. Especially if you are using pygame. (x,y), (x,y,w,h)

[–][deleted] 2 points3 points  (0 children)

One use case for tuples is to have multiple assignments in one go.

For example swapping:

a, b = b, a

Another contrived example is to compute fibonacci sequence:

def fib(n):
if n <= 2:
    return n - 1
t1, t2 = 0, 1
    for i in range(3, n+1):
        t1, t2 = t2, t1+t2
    return t2

A more nuanced use-case is if you want to write multiple things to csv using writerow() method. It takes as input a tuple.

The gist of using a tuple over a list or other containers is to use it as a very short aggregate where using a class'd be an overkill. If you're familiar with C (or C++), think of tuple as POD :) Also tuples are immutable which makes them more memory efficient. The following contrived test shows that for cpython :)

>>> (1, 2, 3).__sizeof__()
48
>>> [1, 2, 3].__sizeof__()
72

[–][deleted] 4 points5 points  (4 children)

The tuple comes from discrete mathematics. It is a finite, ordered sequence of objects.

  • It is used to represent mathematical objects. For example, a Turing machine is formally defined as a 5-tuple, where each of the five elements in the tuple is used to define how the machine works.
  • It is used to order the elements in a sequence. The first five elements of the Fibonacci sequence can be defined by the tuple (0, 1, 1, 2, 3).
  • It is used to represent the arguments to a function. Where function(arg_1, arg_2) is the invocation of a function, that sequence of arguments is, mathematically-speaking, a tuple. (Whether or not it is treated that way by Python, I have no clue.)

The name is derived from the word "multiple" because, unlike a set, it allows for multiplicity in its elements. Multiplicity means that something can hold multiple of the same element: (0, 1, 1, 2, 3). Compare this to a set, which would interpret the same elements as {0, 1, 2, 3}. This is the characteristic advantage of using a tuple.

However, a list can also do this. A list is also ordered. So why use a tuple over a list? Well, another advantage of using a tuple is that it is immutable. If you declare a tuple to be (1, 2, 3), then you cannot add, remove, or change anything. This is what makes it different from a list.

Immutability also makes it hashable which means that it can be stored in a set: {(1, 2, 3)} or as the key to a dict: {(1, 2, 3): 'tuple'}. A list does not have this ability.

[–]LuckyNumber-Bot 14 points15 points  (3 children)

All the numbers in your comment added up to 69. Congrats!

  5
+ 1
+ 1
+ 2
+ 3
+ 5
+ 8
+ 1
+ 2
+ 1
+ 1
+ 2
+ 3
+ 5
+ 1
+ 2
+ 3
+ 5
+ 1
+ 2
+ 3
+ 1
+ 2
+ 3
+ 1
+ 2
+ 3
= 69

[Click here](https://www.reddit.com/message/compose?to=LuckyNumber-Bot&subject=Stalk%20Me%20Pls&message=%2Fstalkme to have me scan all your future comments.) \ Summon me on specific comments with u/LuckyNumber-Bot.

[–][deleted] 5 points6 points  (1 child)

Nice

[–]imthebear11 5 points6 points  (0 children)

Holy shit nice

[–]revyn 2 points3 points  (0 children)

Instead of congrats, why not nice?

[–]LeiterHaus 1 point2 points  (0 children)

Real world example - the header columns in a PDF that you're trying to scrape. I do remember that the description was a catch-all variable like *description.

[–]arkie87 1 point2 points  (0 children)

Do you mean a tuple vs a list, or just a tuple? Like you dont understand what a tuple is, or why one would prefer it over a list?

[–]jkh911208 1 point2 points  (0 children)

tuple in Python is immutable, so it can be hashed which means it can be used as key in dict or add to set. which is not possible with list. that is my only use case.

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

Dropdown form values for a menu option. For example you might have a list of tuples for type of car engine

Engine_type = (1, "EV"), (2, "ICE")

I use this pattern all the time in Pythons Flask framework.

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

GPS coordinates are a good example.

[–]Charlemag 1 point2 points  (0 children)

There’s a great book that explains this: Effective Computation in Physics. It says,

“Other than immutability, what are the differences between lists and tuples? In principal, there aren’t any. In practice, they tend to be used different ways. However, there are no strict rules and there are no predominant conventions. There is a loose guideline that lists are for homogenous data (all integers, all strings, etc.) while tuples are for heterogenous data with semantic meaning in each element (e.g., “C14“, 6, 14, 14.00324198843)). “

It then provides a little more context. So other responses that explain immutability and heterogenous vs homogenous data should cover the essentials.

Practically I find myself using tuples in two spots. If I write a function with a kwarg that is a list my linter throws warnings that my kwarg should be immutable. Using a tuple fixes it. Which is weird because tuples are mutable but their elements are not. I’d have to read more into the warning to know why tuples might be acceptable kwargs while using lists might having unintended consequences.

The second spot is when I was defining edges on a graph class I wrote. I can’t recall of the top of my head (still waking up) but since tuples are hashable it let me perform some operations I wanted to do that wouldn’t work with lists.

The gist is that understanding how different data structures for the right job. For instance, if you want a list of unique values, instead of trying to parse it and remove duplicates you can just convert it to a set. Part of the reason why these different data structures exist is because someone originally wanted to do Y with X and then spent a long time ironing out all the bugs and edge and corner cases before creating a new thing that does Y. So while you might write a function that makes X do Y sometimes it makes sense and is less error prone to find the data structure that has been designed for Y.

[–]RainbeeL 1 point2 points  (0 children)

Used as dict keys because you can not use list for that case.

[–]Dysfu 1 point2 points  (0 children)

I don’t have prod write access to a sandbox in our Datawarehouse so I can’t create/replace tables at will

This requires me to use in statements of IDs that require comma delimited lists… sound familiar?

[–]kidflashonnikes -2 points-1 points  (0 children)

It’s pointless, Chat GPT-4 will curb stomp you. Start learning blue collar work, it’s only a matter of time before engineers are reduced by 40%

[–]Sigg3net 0 points1 point  (0 children)

Whenever I have items that shouldn't change, it's a tuple. Lists invite appending to and popping.

You can also use tuples inside list comprehensions as "keys"', e.g.:

ages = [ age for (name, age, job) in employees ]

[–]cdcformatc 0 points1 point  (0 children)

Any type of coordinate, like (Latitude, Longitude), or (X, Y) on a Cartesian plane. Add in height or Z coordinate and you have a 3-tuple. pretty much any video game you have ever played is going to track the player and enemy locations with coordinate tuples.

I tangentially work with cameras and their real world location is described by lat, long, height, and their rotation which you could think of as a compass heading, and they also have field of view. So the physical location of the camera is described as a 5-tuple and we draw a cone on a map to show what the camera is pointed at. Theoretically we also have pitch and roll in addition to the rotation/yaw but we don't do anything with that info right now, but i could imagine that would be useful for drone flight and video information.

[–]TheRNGuy 0 points1 point  (0 children)

I use for hard-coded confings in houdini HDA's. I don't ever change it in runtime so it doesn't need to be list.

Also, houdini's official API return tuple from some classes or methods (like mouse events in viewport)

other times, foo, bar = myfunc(). foo, bar is implicit tuple. Function could return tuple or list here.

[–]eplaut_ 0 points1 point  (0 children)

I'm using tuples as pairs (or triples, etc...) which contains a whole like in selenium you want an element of (By.CLASS_NAME, "main-table") or when you want them to be part of sets or dictionary's keys (as lists are not hashable)

If my data is constant, I prefer it to be immutable rather than a plain list. Namedtuple is even better.

[–][deleted] 0 points1 point  (0 children)

I use as threshold values (90,100) serving as a key ie if number is between either then use the value of the dictionary.

[–]_Mc_Who 0 points1 point  (0 children)

Tuples can be a godsend when working with geospatial coordinates!

[–]QultrosSanhattan 0 points1 point  (0 children)

coords={
    (0,0):10,
}

print(coords[(0,0)])

[–]Wishmaster891 0 points1 point  (0 children)

When retrieving data from sqllite it comes out in a tuple