Skip to content

suzaku/plain_obj

Repository files navigation

plain_obj

image

image

A faster alternative to namedtuple.

Basic Usage

Creation

import plain_obj
Config = plain_obj.new_type('Config', 'is_debug, skips_dist, run_tests')
config = Config(True, False, True)
if config.is_debug:
    print("This is a verbose debugging message.")

Make a dict

config.as_dict()

Unpacking

is_debug, _, run_tests = config

When to use plain_obj instead of namedtuple?

When faster creation time matters to you.

Comparing plain_obj with namedtuple in Python 2.7:

In [3]: %timeit collections.namedtuple('Point', ['x', 'y', 'z'])
1000 loops, best of 3: 338 µs per loop

In [4]: %timeit plain_obj.new_type('Point', ['x', 'y', 'z'])
10000 loops, best of 3: 97.8 µs per loop

In [5]: Point = collections.namedtuple('Point', ['x', 'y', 'z'])

In [6]: NewPoint = plain_obj.new_type('Point', ['x', 'y', 'z'])

In [7]: %timeit Point(1, 2, 3)
The slowest run took 7.99 times longer than the fastest. This could mean that an intermediate result is being cached.
1000000 loops, best of 3: 507 ns per loop

In [8]: %timeit NewPoint(1, 2, 3)
The slowest run took 6.70 times longer than the fastest. This could mean that an intermediate result is being cached.
1000000 loops, best of 3: 462 ns per loop

In [9]: p = Point(1, 2, 3)

In [10]: new_p = NewPoint(1, 2, 3)

In [11]: %timeit p.x, p.y, p.z
The slowest run took 9.92 times longer than the fastest. This could mean that an intermediate result is being cached.
1000000 loops, best of 3: 408 ns per loop

In [12]: %timeit new_p.x, new_p.y, new_p.z
The slowest run took 11.70 times longer than the fastest. This could mean that an intermediate result is being cached.
10000000 loops, best of 3: 163 ns per loop

Comparing plain_obj with namedtuple in Python 3.6:

In [3]: %timeit collections.namedtuple('Point', ['x', 'y', 'z'])
382 µs ± 3.82 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [4]: %timeit plain_obj.new_type('Point', ['x', 'y', 'z'])
53.5 µs ± 1.2 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [5]: Point = collections.namedtuple('Point', ['x', 'y', 'z'])

In [6]: NewPoint = plain_obj.new_type('Point', ['x', 'y', 'z'])

In [7]: %timeit Point(1, 2, 3)
521 ns ± 2.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [8]: %timeit NewPoint(1, 2, 3)
438 ns ± 5.53 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [9]: p = Point(1, 2, 3)

In [10]: new_p = NewPoint(1, 2, 3)

In [11]: %timeit p.x, p.y, p.z
282 ns ± 2.52 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [12]: %timeit new_p.x, new_p.y, new_p.z
148 ns ± 1.7 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

As you can see, it's faster in all cases including type creation, object instantiation and attribute access.

Sponsor