Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions python-namedtuple/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Write Pythonic and Clean Code With namedtuple

This folder provides the code examples for the Real Python tutorial [Write Pythonic and Clean Code With namedtuple](https://realpython.com/python-namedtuple/).
6 changes: 6 additions & 0 deletions python-namedtuple/employees.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
name,job,email
"Linda","Technical Lead","[email protected]"
"Joe","Senior Web Developer","[email protected]"
"Lara","Project Manager","[email protected]"
"David","Data Analyst","[email protected]"
"Jane","Senior Python Developer","[email protected]"
9 changes: 9 additions & 0 deletions python-namedtuple/employees.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import csv
from collections import namedtuple

with open("employees.csv", "r", encoding="utf-8") as csv_file:
reader = csv.reader(csv_file)
Employee = namedtuple("Employee", next(reader), rename=True)
for row in reader:
employee = Employee(*row)
print(employee.name, employee.job, employee.email)
21 changes: 21 additions & 0 deletions python-namedtuple/namedtuple_dataclass_memory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from collections import namedtuple
from dataclasses import dataclass

from pympler import asizeof

PointNamedTuple = namedtuple("PointNamedTuple", "x y z")


@dataclass
class PointDataClass:
x: int
y: int
z: int


namedtuple_memory = asizeof.asizeof(PointNamedTuple(x=1, y=2, z=3))
dataclass_memory = asizeof.asizeof(PointDataClass(x=1, y=2, z=3))
gain = 100 - namedtuple_memory / dataclass_memory * 100

print(f"namedtuple: {namedtuple_memory} bytes ({gain:.2f}% lower)")
print(f"data class: {dataclass_memory} bytes")
36 changes: 36 additions & 0 deletions python-namedtuple/namedtuple_dataclass_time.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from collections import namedtuple
from dataclasses import dataclass
from time import perf_counter


def average_time(structure, test_func):
time_measurements = []
for _ in range(1_000_000):
start = perf_counter()
test_func(structure)
end = perf_counter()
time_measurements.append(end - start)
return sum(time_measurements) / len(time_measurements) * int(1e9)


def time_structure(structure):
structure.x
structure.y
structure.z


PointNamedTuple = namedtuple("PointNamedTuple", "x y z", defaults=[3])


@dataclass
class PointDataClass:
x: int
y: int
z: int


namedtuple_time = average_time(PointNamedTuple(x=1, y=2, z=3), time_structure)
dataclass_time = average_time(PointDataClass(x=1, y=2, z=3), time_structure)

print(f"namedtuple: {namedtuple_time:.2f} ns")
print(f"data class: {dataclass_time:.2f} ns")
13 changes: 13 additions & 0 deletions python-namedtuple/namedtuple_dict_memory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from collections import namedtuple

from pympler import asizeof

Point = namedtuple("Point", "x y z")
point = Point(1, 2, 3)

namedtuple_size = asizeof.asizeof(point)
dict_size = asizeof.asizeof(point._asdict())
gain = 100 - namedtuple_size / dict_size * 100

print(f"namedtuple: {namedtuple_size} bytes ({gain:.2f}% smaller)")
print(f"dict: {dict_size} bytes")
39 changes: 39 additions & 0 deletions python-namedtuple/namedtuple_dict_time.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from collections import namedtuple
from time import perf_counter


def average_time(structure, test_func):
time_measurements = []
for _ in range(1_000_000):
start = perf_counter()
test_func(structure)
end = perf_counter()
time_measurements.append(end - start)
return sum(time_measurements) / len(time_measurements) * int(1e9)


def time_dict(dictionary):
"x" in dictionary
"missing_key" in dictionary
2 in dictionary.values()
"missing_value" in dictionary.values()
dictionary["y"]


def time_namedtuple(named_tuple):
"x" in named_tuple._fields
"missing_field" in named_tuple._fields
2 in named_tuple
"missing_value" in named_tuple
named_tuple.y


Point = namedtuple("Point", "x y z")
point = Point(x=1, y=2, z=3)

namedtuple_time = average_time(point, time_namedtuple)
dict_time = average_time(point._asdict(), time_dict)
gain = dict_time / namedtuple_time

print(f"namedtuple: {namedtuple_time:.2f} ns ({gain:.2f}x faster)")
print(f"dict: {dict_time:.2f} ns")
51 changes: 51 additions & 0 deletions python-namedtuple/performance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from collections import namedtuple

STest = namedtuple("TEST", "a b c")
a = STest(a=1, b=2, c=3)


class Test(object):
__slots__ = ["a", "b", "c"]

def __init__(self) -> None:
self.a = 1
self.b = 2
self.c = 3


b = Test()

c = {"a": 1, "b": 2, "c": 3}

d = (1, 2, 3)
e = [1, 2, 3]
f = (1, 2, 3)
g = [1, 2, 3]
key = 2

if __name__ == "__main__":
from timeit import timeit

print("Named tuple with a, b, c:")
print(timeit("z = a.c", "from __main__ import a"))

print("Named tuple, using index:")
print(timeit("z = a[2]", "from __main__ import a"))

print("Class using __slots__, with a, b, c:")
print(timeit("z = b.c", "from __main__ import b"))

print("Dictionary with keys a, b, c:")
print(timeit("z = c['c']", "from __main__ import c"))

print("Tuple with three values, using a constant key:")
print(timeit("z = d[2]", "from __main__ import d"))

print("List with three values, using a constant key:")
print(timeit("z = e[2]", "from __main__ import e"))

print("Tuple with three values, using a local key:")
print(timeit("z = d[key]", "from __main__ import d, key"))

print("List with three values, using a local key:")
print(timeit("z = e[key]", "from __main__ import e, key"))
13 changes: 13 additions & 0 deletions python-namedtuple/person_dataclass.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from dataclasses import astuple, dataclass


@dataclass
class Person:
name: str
age: int
height: float
weight: float
country: str = "Canada"

def __iter__(self):
return iter(astuple(self))
26 changes: 26 additions & 0 deletions python-namedtuple/subclass.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from collections import namedtuple
from datetime import date

BasePerson = namedtuple(
"BasePerson", "name birthdate country", defaults=["Canada"]
)


class Person(BasePerson):
"""A namedtuple subclass to hold a person's data."""

__slots__ = ()

def __repr__(self):
return f"Name: {self.name}, age: {self.age} years old."

@property
def age(self):
return (date.today() - self.birthdate).days // 365


Person.__doc__

jane = Person("Jane", date(1996, 3, 5))
print(jane.age)
print(jane)
32 changes: 32 additions & 0 deletions python-namedtuple/tuple_namedtuple_time.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from collections import namedtuple
from time import perf_counter


def average_time(test_func):
time_measurements = []
for _ in range(1_000):
start = perf_counter()
test_func()
end = perf_counter()
time_measurements.append(end - start)
return sum(time_measurements) / len(time_measurements) * int(1e9)


def time_tuple():
tuple([1] * 1000)


fields = [f"a{n}" for n in range(1000)]
TestNamedTuple = namedtuple("TestNamedTuple", fields)


def time_namedtuple():
TestNamedTuple(*([1] * 1000))


namedtuple_time = average_time(time_namedtuple)
tuple_time = average_time(time_tuple)
gain = namedtuple_time / tuple_time

print(f"tuple: {tuple_time:.2f} ns ({gain:.2f}x faster)")
print(f"namedtuple: {namedtuple_time:.2f} ns")
19 changes: 19 additions & 0 deletions python-namedtuple/typed_namedtuple_memory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from collections import namedtuple
from typing import NamedTuple

from pympler import asizeof

PointNamedTuple = namedtuple("PointNamedTuple", "x y z")


class PointTypedNamedTuple(NamedTuple):
x: int
y: int
z: int


namedtuple_memory = asizeof.asizeof(PointNamedTuple(x=1, y=2, z=3))
typed_namedtuple_memory = asizeof.asizeof(PointTypedNamedTuple(x=1, y=2, z=3))

print(f"namedtuple: {namedtuple_memory} bytes")
print(f"typing.NamedTuple: {typed_namedtuple_memory} bytes")
40 changes: 40 additions & 0 deletions python-namedtuple/typed_namedtuple_time.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from collections import namedtuple
from time import perf_counter
from typing import NamedTuple


def average_time(structure, test_func):
time_measurements = []
for _ in range(1_000_000):
start = perf_counter()
test_func(structure)
end = perf_counter()
time_measurements.append(end - start)
return sum(time_measurements) / len(time_measurements) * int(1e9)


def time_structure(structure):
"x" in structure._fields
"missing_field" in structure._fields
2 in structure
"missing_value" in structure
structure.y


PointNamedTuple = namedtuple("PointNamedTuple", "x y z")


class PointTypedNamedTuple(NamedTuple):
x: int
y: int
z: int


namedtuple_time = average_time(PointNamedTuple(x=1, y=2, z=3), time_structure)
typed_namedtuple_time = average_time(
PointTypedNamedTuple(x=1, y=2, z=3), time_structure
)
gain = typed_namedtuple_time / namedtuple_time

print(f"namedtuple: {namedtuple_time:.2f} ns")
print(f"typing.NamedTuple: {typed_namedtuple_time:.2f} ns")