Skip to content

Commit 78dc8f3

Browse files
authored
Merge pull request #673 from realpython/python-namedtuple
Sample code for the article on namedtuple
2 parents f9d23e8 + ced1603 commit 78dc8f3

13 files changed

+308
-0
lines changed

python-namedtuple/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Write Pythonic and Clean Code With namedtuple
2+
3+
This folder provides the code examples for the Real Python tutorial [Write Pythonic and Clean Code With namedtuple](https://realpython.com/python-namedtuple/).

python-namedtuple/employees.csv

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
name,job,email
2+
"Linda","Technical Lead","[email protected]"
3+
"Joe","Senior Web Developer","[email protected]"
4+
"Lara","Project Manager","[email protected]"
5+
"David","Data Analyst","[email protected]"
6+
"Jane","Senior Python Developer","[email protected]"

python-namedtuple/employees.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import csv
2+
from collections import namedtuple
3+
4+
with open("employees.csv", "r", encoding="utf-8") as csv_file:
5+
reader = csv.reader(csv_file)
6+
Employee = namedtuple("Employee", next(reader), rename=True)
7+
for row in reader:
8+
employee = Employee(*row)
9+
print(employee.name, employee.job, employee.email)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from collections import namedtuple
2+
from dataclasses import dataclass
3+
4+
from pympler import asizeof
5+
6+
PointNamedTuple = namedtuple("PointNamedTuple", "x y z")
7+
8+
9+
@dataclass
10+
class PointDataClass:
11+
x: int
12+
y: int
13+
z: int
14+
15+
16+
namedtuple_memory = asizeof.asizeof(PointNamedTuple(x=1, y=2, z=3))
17+
dataclass_memory = asizeof.asizeof(PointDataClass(x=1, y=2, z=3))
18+
gain = 100 - namedtuple_memory / dataclass_memory * 100
19+
20+
print(f"namedtuple: {namedtuple_memory} bytes ({gain:.2f}% lower)")
21+
print(f"data class: {dataclass_memory} bytes")
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from collections import namedtuple
2+
from dataclasses import dataclass
3+
from time import perf_counter
4+
5+
6+
def average_time(structure, test_func):
7+
time_measurements = []
8+
for _ in range(1_000_000):
9+
start = perf_counter()
10+
test_func(structure)
11+
end = perf_counter()
12+
time_measurements.append(end - start)
13+
return sum(time_measurements) / len(time_measurements) * int(1e9)
14+
15+
16+
def time_structure(structure):
17+
structure.x
18+
structure.y
19+
structure.z
20+
21+
22+
PointNamedTuple = namedtuple("PointNamedTuple", "x y z", defaults=[3])
23+
24+
25+
@dataclass
26+
class PointDataClass:
27+
x: int
28+
y: int
29+
z: int
30+
31+
32+
namedtuple_time = average_time(PointNamedTuple(x=1, y=2, z=3), time_structure)
33+
dataclass_time = average_time(PointDataClass(x=1, y=2, z=3), time_structure)
34+
35+
print(f"namedtuple: {namedtuple_time:.2f} ns")
36+
print(f"data class: {dataclass_time:.2f} ns")
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from collections import namedtuple
2+
3+
from pympler import asizeof
4+
5+
Point = namedtuple("Point", "x y z")
6+
point = Point(1, 2, 3)
7+
8+
namedtuple_size = asizeof.asizeof(point)
9+
dict_size = asizeof.asizeof(point._asdict())
10+
gain = 100 - namedtuple_size / dict_size * 100
11+
12+
print(f"namedtuple: {namedtuple_size} bytes ({gain:.2f}% smaller)")
13+
print(f"dict: {dict_size} bytes")
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from collections import namedtuple
2+
from time import perf_counter
3+
4+
5+
def average_time(structure, test_func):
6+
time_measurements = []
7+
for _ in range(1_000_000):
8+
start = perf_counter()
9+
test_func(structure)
10+
end = perf_counter()
11+
time_measurements.append(end - start)
12+
return sum(time_measurements) / len(time_measurements) * int(1e9)
13+
14+
15+
def time_dict(dictionary):
16+
"x" in dictionary
17+
"missing_key" in dictionary
18+
2 in dictionary.values()
19+
"missing_value" in dictionary.values()
20+
dictionary["y"]
21+
22+
23+
def time_namedtuple(named_tuple):
24+
"x" in named_tuple._fields
25+
"missing_field" in named_tuple._fields
26+
2 in named_tuple
27+
"missing_value" in named_tuple
28+
named_tuple.y
29+
30+
31+
Point = namedtuple("Point", "x y z")
32+
point = Point(x=1, y=2, z=3)
33+
34+
namedtuple_time = average_time(point, time_namedtuple)
35+
dict_time = average_time(point._asdict(), time_dict)
36+
gain = dict_time / namedtuple_time
37+
38+
print(f"namedtuple: {namedtuple_time:.2f} ns ({gain:.2f}x faster)")
39+
print(f"dict: {dict_time:.2f} ns")

python-namedtuple/performance.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
from collections import namedtuple
2+
3+
STest = namedtuple("TEST", "a b c")
4+
a = STest(a=1, b=2, c=3)
5+
6+
7+
class Test(object):
8+
__slots__ = ["a", "b", "c"]
9+
10+
def __init__(self) -> None:
11+
self.a = 1
12+
self.b = 2
13+
self.c = 3
14+
15+
16+
b = Test()
17+
18+
c = {"a": 1, "b": 2, "c": 3}
19+
20+
d = (1, 2, 3)
21+
e = [1, 2, 3]
22+
f = (1, 2, 3)
23+
g = [1, 2, 3]
24+
key = 2
25+
26+
if __name__ == "__main__":
27+
from timeit import timeit
28+
29+
print("Named tuple with a, b, c:")
30+
print(timeit("z = a.c", "from __main__ import a"))
31+
32+
print("Named tuple, using index:")
33+
print(timeit("z = a[2]", "from __main__ import a"))
34+
35+
print("Class using __slots__, with a, b, c:")
36+
print(timeit("z = b.c", "from __main__ import b"))
37+
38+
print("Dictionary with keys a, b, c:")
39+
print(timeit("z = c['c']", "from __main__ import c"))
40+
41+
print("Tuple with three values, using a constant key:")
42+
print(timeit("z = d[2]", "from __main__ import d"))
43+
44+
print("List with three values, using a constant key:")
45+
print(timeit("z = e[2]", "from __main__ import e"))
46+
47+
print("Tuple with three values, using a local key:")
48+
print(timeit("z = d[key]", "from __main__ import d, key"))
49+
50+
print("List with three values, using a local key:")
51+
print(timeit("z = e[key]", "from __main__ import e, key"))
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from dataclasses import astuple, dataclass
2+
3+
4+
@dataclass
5+
class Person:
6+
name: str
7+
age: int
8+
height: float
9+
weight: float
10+
country: str = "Canada"
11+
12+
def __iter__(self):
13+
return iter(astuple(self))

python-namedtuple/subclass.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from collections import namedtuple
2+
from datetime import date
3+
4+
BasePerson = namedtuple(
5+
"BasePerson", "name birthdate country", defaults=["Canada"]
6+
)
7+
8+
9+
class Person(BasePerson):
10+
"""A namedtuple subclass to hold a person's data."""
11+
12+
__slots__ = ()
13+
14+
def __repr__(self):
15+
return f"Name: {self.name}, age: {self.age} years old."
16+
17+
@property
18+
def age(self):
19+
return (date.today() - self.birthdate).days // 365
20+
21+
22+
Person.__doc__
23+
24+
jane = Person("Jane", date(1996, 3, 5))
25+
print(jane.age)
26+
print(jane)

0 commit comments

Comments
 (0)