Interesting performance of creating objects via normal class, data class and named tuple Interesting performance of creating objects via normal class, data class and named tuple python-3.x python-3.x

Interesting performance of creating objects via normal class, data class and named tuple


Looking here, and referred PEP-557. There is additional method - post_init. To validate that please use @dataclass(init=False) - in that case the method should not be called.

UPDATE

Looks like my initial explanation was a bit naive. tl;dr:

In Python everything is a dict. In case of data class there are more entries in that dict, so in turn that takes more time to put them there.

How that change happened? @Arne's comment spotted that I'm missing something here. I did sample code:

from dataclasses import dataclassimport time@dataclassclass Position:    lon: float = 0.0    lat: float = 0.0start_time = time.time()for i in range(100000):    p = Position(lon=1.0, lat=1.0)elapsed = time.time() - start_timeprint(f"dataclass {elapsed}")print(dir(p))class Position2:    lon: float = 0.0    lat: float = 0.0    def __init__(self, lon, lat):        self.lon = lon        self.lat = latstart_time = time.time()for i in range(100000):    p = Position2(lon=1.0, lat=1.0)elapsed = time.time() - start_timeprint(f"just class {elapsed}")print(dir(p))start_time = time.time()for i in range(100000):    p = {"lon": 1.0, "lat": 1.0}elapsed = time.time() - start_timeprint(f"dict {elapsed}")

With results:

/usr/bin/python3.8 ...../test.pydataclass 0.16358232498168945['__annotations__', '__class__', '__dataclass_fields__', '__dataclass_params__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'lat', 'lon']just class 0.1495649814605713['__annotations__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'lat', 'lon']dict 0.028212785720825195Process finished with exit code 0

Dict example is for reference.

Looked into dataclass, this function:

(489) def _init_fn(fields, frozen, has_post_init, self_name, globals):

is responsible for creation of constructor. As Arne spotted - post_init code is optional, and not generated. I had other idea, that there is some work around fields, but:

In [5]: p = Position(lat = 1.1, lon=2.2)                                                                                                                                                                           In [7]: p.lat.__class__                                                                                                                                                                                            Out[7]: float

so there is no additional wraps / code here. From all of that the only additional stuff I saw - is that more methods.