Innovate | Connect | Transform
ContactService statusUSD $

GICT knowledge base

Python Tuple Basics: Immutability, Unpacking, and Hashable Keys

Published

Python

A Python tuple is an ordered sequence whose item references cannot be replaced, added, or removed after creation. It is useful for a fixed record such as a host and port. Immutability is not the same as encryption, access control, or a guarantee that every object inside the tuple is unchangeable.

The examples below use ordinary Python 3 and require no packages or network access. Each code block is independent unless stated otherwise.

Create a tuple

A comma creates a nonempty tuple. Parentheses group the expression; empty parentheses create the empty tuple. The comma matters when there is only one item.

endpoint = ("web.example", 443)
single = (443,)
not_a_tuple = (443)
empty = ()
assert isinstance(endpoint, tuple)
assert isinstance(single, tuple)
assert isinstance(not_a_tuple, int)
assert len(empty) == 0

You can also convert an iterable: tuple([80, 443]) produces (80, 443). Conversion is shallow; it does not recursively freeze nested lists or dictionaries.

Read, slice, and unpack

endpoint = ("web.example", 443)
host, port = endpoint
print(host)          # web.example
print(endpoint[-1])  # 443
print(endpoint[:1])  # ('web.example',)
assert port == 443

Indexing an absent position raises IndexError. Slicing can return an empty tuple without raising that error. Unpacking into two variables requires exactly two values; use a starred target when the number of remaining items varies.

primary, *fallbacks = ("web-a", "web-b", "web-c")
assert primary == "web-a"
assert fallbacks == ["web-b", "web-c"]

Loop and inspect

ports = (80, 443, 443)
for position, port in enumerate(ports):
    print(position, port)
assert len(ports) == 3
assert ports.count(443) == 2
assert ports.index(443) == 1
assert 22 not in ports

index() returns the first matching position and raises ValueError if the value is absent. Tuples permit duplicates. For more iteration patterns, see How to Loop Through Tuples in Python.

What immutability does and does not mean

endpoint = ("web.example", 443)
try:
    endpoint[1] = 8443
except TypeError:
    print("Tuple items cannot be reassigned")

A mutable item inside a tuple can still change. Another reference to that same item will observe the change:

ports = [80]
record = ("web.example", ports)
ports.append(443)
assert record == ("web.example", [80, 443])

Consequently, wrapping shared mutable data in a tuple does not make it thread-safe or read-only. Choose an appropriate representation and synchronization strategy for the objects you actually share.

Use a tuple as a dictionary key only when it is hashable

A tuple is hashable only if all its items are hashable. A tuple of strings and integers works; one containing a list does not. Hashability, rather than a blanket rule about all immutable objects, is the requirement for a dictionary key or set member. See Python’s definition of hashable.

labels = {("web.example", 443): "HTTPS"}
assert labels[("web.example", 443)] == "HTTPS"
try:
    hash(("web.example", [443]))
except TypeError:
    print("The nested list makes this tuple unhashable")

Return a small record from a function

def default_endpoint():
    return "web.example", 443

host, port = default_endpoint()
assert (host, port) == ("web.example", 443)

For a large record, named fields in a dataclass or named tuple can be clearer than remembering many numeric positions. For a collection that grows or changes frequently, a list is usually more straightforward. Do not choose tuples based on an unmeasured claim that they make every operation faster.

Verification: All eight code blocks were executed with Python 3.12.14, including the expected TypeError cases. The Python tutorial provides the underlying sequence semantics. To replace values without changing the original tuple, see Creating an Updated Tuple in Python.

Related guides

Account services are unavailable until the production backend is configured.