Mastering the Basics of Python Tuples and Named Tuples

Learn how to use Python's Tuple and Named Tuple data structures for efficient and effective data manipulation

Introduction:

Python is a dynamic general-purpose language containing very useful built-in data structures such as Lists, Tuples, dictionaries, sets, and many others. These Data Structures are the building block of modern software systems. So, today we understand Tuples and Named tuples from the object-oriented perspective and their applications.

What are Tuples in the first place?

Let's understand from an example, we all know the 2D graph plot where a point on a graph is defined by X-coordinate and Y-coordinate. e.g if x = 2 and y= 4 then the point will be (2,4).

But if you alter the x and y value then the point will not be in the same position.

So, now you understand changing the (x,y) value will change the position that property is called Immutability means value remains intact in the entire operation. and the (x,y) are called tuples.

That's from the mathematical perspective but what are the Pythonic Tuples?

In Python tuple is an object which contains a specific number of other objects in sequence. Sequence, means python tuples are immutable in nature. So, you can not add, replace, or remove the object on the fly like Array.

Tuples properties with respect to List

If you think it is a massive restriction then think about that why you are using an immutable data structure in the first place, you may use List or Array. The primary benefits of Tuples are immutability like strings, or numbers because it has a hash value which facilitates us to use them as keys to the dictionary. But there is a little nuisance, if a tuple contains a mutable object then that tuple will not contain a hash value because the mutable object has no values.

Let's do some actual coding:

First, create a tuple with company stock value as Name, CURRENT, HIGH, LOW

# Create a tuple with a stock of a company
# type 1
stock = "ABC", 146.23, 65.75, 123.54
# type 2
stock_2 = ("ABC", 146.23, 65.75, 123.54)

print(type(stock))
>>> <class 'tuple'>
print(type(stock_2)
>>> <class 'tuple'>

We can create tuples in both ways but if you want to put tuples in function, generator, or list comprehension then you must use parentheses.

Tuple in a function

import datetime
def mid(stock, date):
    company, cur, high, low = stock
    return (((high + low) / 2), date)

mid(("ABC", 146.85, 65.75, 123.54), datetime.date(2022,12, 31 ))

>>> (94.64500000000001, datetime.date(2022, 12, 31))

You can see in the above output the mid returns a tuple with the middle value and date. So, when python returns any tuple it always returns with (), it is called canonical representation.

let If you want to create a tuple with single values, what will you do?

let's see an actual example of a tuple then you automatically know what will you do.

import numpy as np

X = np.array([[1,2,3],[3,4,5],[4,5,6]])
print(X.shape)

do you know the Output?

print(X.shape)
>>> (3, 3)

now will you tell me the class type of X.shape? I know you understand now. let's see the output.

print(type(X.shape))
>>> <class 'tuple'>

Ah! voila it is a tuple class. Now see another example.

X = np.array([1,2,3])
print(X.shape)
>>> (3,)
print(type(X.shape))
>>> <class 'tuple'>

with this example, you now understand if you want to create a tuple with a single value just put a comma after the value. without the comma, python thinks of it as an "int" type.

single_val_tuple = (5,)
print(type(single_val_tuple))
>>> <class 'tuple'>

How to unpack a tuple?

Unpacking is a useful feature in Python. In the above example in calculating the middle value of the Stock, the first line inside the function unpacks the stock parameter into four different variables. The tuple has to be the same length as the number of the variable otherwise it will raise an exception.

import datetime
def mid(stock, date):
    company, cur, high, low = stock
    return (((high + low) / 2), date)
mid(("ABC", 146.85, 65.75, 123.54, 123), datetime.date(2022,12, 31 ))

>>> ValueError: too many values to unpack (expected 4)

it will give a ValueError.

You can use index slicing to unpack values from tuples as stock[1:3] and stock[:-1]

Now with these advantages of tuples, one of the major disadvantages of tuples is readability. If someone wants to know what is in position 2 of the specific tuple? They can guess it by the name we assign it such as HIGH, or Low. But if you need the value in the middle of a calculation without assigning it, then how?

If you think of indexing then think of in the middle of the program a slice index comes up here and there, this opacity in the software development leads to many bugs and frustrated debugging in the future who may work after you. So, we need a better way of handling tuples.

Named Tuples from Typing module.

So, what do we do when we need the values of the tuple frequently?

  • A Named Tuple is a tuple with some added benefits in an object-oriented way. They are a great way to create an immutable grouping of data.

  • When we define a Named Tuple we are creating a subclass of typing.NamedTuple, based on the list of names and data types.

  • We don't need to create an __init__() method, it will create for us.

We will re-create the Stock tuple with Named tuples.

from typing import NamedTuple
class Stock(NamedTuple):
    company: str
    current: float
    high: float
    low: float

s1 = Stock("ABC", 146.85, 65.75, 123.54)
s2 = Stock("ABC", 146.85, high=65.75, low=123.54)

print(s1.company)
>>> 'ABC'
print(s2.high)
>>> 65.75

Python is a dynamic language, it is not statically typed like Java, Scala, or C++. So, The above Typing module is a type annotation module for Python variables and functions. Then the Stock class inherits the NamedTuple class and creates the Stock Tuple using parameters.

The NamedTuple Stock class has many benefits over normal Tuples. It includes

  1. __init__()

  2. __repr__()

  3. __eq__()

  4. __hash__()

If you are familiar with OOP concepts then you know the above double underscore method also called the dunder method is a special function for an Object in Python.

# __repr__() is similar to print() in normal conditions.
s2.__repr__()
>>> "Stock(company='ABC', current=146.85, high=65.75, low=123.54)"
print(s2)
>>> Stock(company='ABC', current=146.85, high=65.75, low=123.54)

The difference between __repr__() and print() is that __repr__() return 'str' Type and print() will return 'NoneType'

Similarly __eq__() is similar to == operator

s1.__eq__(s2)
>>> True
s1 == s2
>>> True

And __hash__() will return the Hash value of the Tuple

s1.__hash__()
>>> 4957183021980313404
s2.__hash__()
>>> 4957183021980313404

You can see that both hash values are the same which means s1 and s2 tuples are the representation of the same Stock Object.

Now if you want to calculate the middle value from the Stock, it is easy peasy with @property decorator. Which adds a property or method to the Stock class.

from typing import NamedTuple
class Stock(NamedTuple):
    company: str
    current: float
    high: float
    low: float
    @property
    def mid(self):
        return (self.high + self.low)/2

s1 = Stock("ABC", 146.85, 65.75, 123.54)

Now mid() method is a part of the Stock class definition.

s1.mid
>>> 94.64500000000001

Key takeaway

  1. The Tuple is a built-in immutable data structure in Python.

  2. Tuple has many real-life applications such shape of a matrix, tensor, and color value of an image (255,255,255).

  3. Tuple can be used to supply keys for the dictionary.

  4. A named tuple from typing module is a tuple with added benefits.

  5. Named Tuple has __init__(), __repr__(), __eq__(), __hash__() included.

  6. We can not change the Named tuple state but can compute values derived from the current state using @property decorator in class.

Let's Connect

Thank you for reading my article.

If you like my article follow me and share my article.☺️