Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork33.3k
Description
Feature or enhancement
Thedataclasses library provides an easy way to create classes. The library will automatically generate relevant methods for the users.
Creatingdataclasses with argumentfrozen=True will automatically generate methods__setattr__ and__delattr__ in_frozen_get_del_attr.
This issue proposes to change thetuple-based lookup toset-based lookup. Reduce the time complexity from
In [1]:# tuple-basedIn [2]:%timeit'a'in ('a','b','c','d','e','f','g')9.91ns ±0.0982nsperloop (mean ±std.dev.of7runs,100,000,000loopseach)In [3]:%timeit'd'in ('a','b','c','d','e','f','g')33.2ns ±0.701nsperloop (mean ±std.dev.of7runs,10,000,000loopseach)In [4]:%timeit'g'in ('a','b','c','d','e','f','g')56.4ns ±0.818nsperloop (mean ±std.dev.of7runs,10,000,000loopseach)In [5]:# set-basedIn [6]:%timeit'a'in {'a','b','c','d','e','f','g'}11.3ns ±0.0723nsperloop (mean ±std.dev.of7runs,100,000,000loopseach)In [7]:%timeit'd'in {'a','b','c','d','e','f','g'}11ns ±0.106nsperloop (mean ±std.dev.of7runs,100,000,000loopseach)In [8]:%timeit'g'in {'a','b','c','d','e','f','g'}11.1ns ±0.126nsperloop (mean ±std.dev.of7runs,100,000,000loopseach)
A tiny benchmark script:
fromcontextlibimportsuppressfromdataclassesimportFrozenInstanceError,dataclass@dataclass(frozen=True)classFoo2:a:intb:intfoo2=Foo2(1,2)defbench2(inst):withsuppress(FrozenInstanceError):inst.a=0withsuppress(FrozenInstanceError):inst.b=0@dataclass(frozen=True)classFoo7:a:intb:intc:intd:inte:intf:intg:intfoo7=Foo7(1,2,3,4,5,6,7)defbench7(inst):withsuppress(FrozenInstanceError):inst.a=0withsuppress(FrozenInstanceError):inst.b=0withsuppress(FrozenInstanceError):inst.c=0withsuppress(FrozenInstanceError):inst.d=0withsuppress(FrozenInstanceError):inst.e=0withsuppress(FrozenInstanceError):inst.f=0withsuppress(FrozenInstanceError):inst.g=0classBar(Foo7):def__init__(self,a,b,c,d,e,f,g):super().__init__(a,b,c,d,e,f,g)self.baz=0defbench(inst):inst.baz=1
Result:
set-based lookup:
In [2]:%timeitbench2(foo2)1.08µs ±28.1nsperloop (mean ±std.dev.of7runs,1,000,000loopseach)In [3]:%timeitbench7(foo7)3.81µs ±20.3nsperloop (mean ±std.dev.of7runs,100,000loopseach)In [4]:%timeitbench(bar)249ns ±6.31nsperloop (mean ±std.dev.of7runs,1,000,000loopseach)
tuple-based lookup (original):
In [2]:%timeitbench2(foo2)1.15µs ±10.9nsperloop (mean ±std.dev.of7runs,1,000,000loopseach)In [3]:%timeitbench7(foo7)3.97µs ±15.7nsperloop (mean ±std.dev.of7runs,100,000loopseach)In [4]:%timeitbench(bar)269ns ±4.09nsperloop (mean ±std.dev.of7runs,1,000,000loopseach)
Result:`set`-based lookup:```pythonIn [2]: %timeit bench2(foo2)1.08 µs ± 28.1 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)In [3]: %timeit bench7(foo7)3.81 µs ± 20.3 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)tuple-based lookup (original):
In [2]:%timeitbench2(foo2)1.15µs ±10.9nsperloop (mean ±std.dev.of7runs,1,000,000loopseach)In [3]:%timeitbench7(foo7)3.97µs ±15.7nsperloop (mean ±std.dev.of7runs,100,000loopseach)
Theset-based is constantly faster than the old approach. And the theoretical time complexity is also smaller (
Ref:#102573
Pitch
(Explain why this feature or enhancement should be implemented and how it would be used.
Add examples, if applicable.)
In the autogenerate__setattr__ and__delattr__, they have a sanity check at the beginning of the method. For example:
def__setattr__(self,name,value):iftype(self)is {{UserType}}ornamein ({{atupleoffieldnames}}):raiseFrozenInstanceError(f"cannot assign to field{name!r}")super(cls,self).__setattr__(name,value)
If someone inherits the frozen dataclass, the sanity check will taketuple__contains__(...) and finally callssuper().__setattr__(...). For example:
@dataclass(frozen=True)classFrozenBase:x:inty:int ...# N_FIELDSclassFoo(FrozenBase):def__init__(self,x,y,somevalue,someothervalue):super().__init__(x,y)self.somevalue=somevalue# takes O(N_FIELDS)self.someothervalue=someothervalue# takes O(N_FIELDS) time againfoo=Foo(1,2,3,4)foo.extravalue=extravalue# takes O(N_FIELDS) time again
Previous discussion
N/A.