Getters and Setters
Andrew Dalke
dalke at bioreason.com
Thu Jul 15 03:30:41 EDT 1999
More information about the Python-list mailing list
Thu Jul 15 03:30:41 EDT 1999
- Previous message (by thread): Getters and Setters
- Next message (by thread): Getters and Setters
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Neil Schemenauer <nascheme at ucalgary.ca> proposed: > # A mixin class to automagicly create getter/setter methods for > # class attributes. > class GetterSetter: > def __getattr__(self, name): > try: > if name[:3] == 'get': > return _Getter(self.__dict__[name[3:]]) > elif name[:3] == 'set': > return _Setter(self, name[3:]) > except KeyError: > pass > raise AttributeError Here's one optimization, don't recreate getter/setter objects. How about this: class GetterSetter: def __getattr__(self, name): try: if name[:3] == 'get': x = _Getter(self.__dict__[name[3:]]) self.__dict__[name] = x return x elif name[:3] == 'set': x = _Setter(self, name[3:]) self.__dict__[name] = x return x except KeyError: pass raise AttributeError For your timing test that reduced the overhead to a factor of about 2.5 (I upped n to 10000 to get better times on my machine): Your version val> python getattr.py Comparing 10000 iterations GetterSetter 1.78183102608 explicit function 0.201846957207 Cached objects val> python getattr.py Comparing 10000 iterations GetterSetter 0.455724000931 explicit function 0.207740902901 Still, you're doing this __call__ lookup every time, so why not use something which doesn't need that lookup, like a function? My first thought was to create a lambda function or a function in local scope, but the argument passing trick (lamba val=val: val) would mess up the API by allowing an argument to be passed in. Since your timing test does so many repeats (esp. when I increase n :), that suggests I can take a 1-time cost per attribute, so I can use an eval. class GetterSetter: def __getattr__(self, name): try: if name[:3] == 'get': #x = _Getter(self.__dict__[name[3:]]) val = self.__dict__[name[3:]] x = eval("lambda :x", {"x": val}) self.__dict__[name] = x return x elif name[:3] == 'set': x = _Setter(self, name[3:]) self.__dict__[name] = x return x except KeyError: pass raise AttributeError val> python getattr.py Comparing 10000 iterations GetterSetter 0.359815955162 explicit function 0.210291981697 Now, only 70% slower. For my final trick, I'll do the same to the Setter: import operator class GetterSetter: def __getattr__(self, name): try: if name[:3] == 'get': #x = _Getter(self.__dict__[name[3:]]) val = self.__dict__[name[3:]] x = eval("lambda :x", {"x": val}) self.__dict__[name] = x return x elif name[:3] == 'set': x = eval("lambda val: set(dict, %s, val)" % `name[3:]`, {"dict": self.__dict__, "set": operator.setitem}) #x = _Setter(self, name[3:]) self.__dict__[name] = x return x except KeyError: pass raise AttributeError Notice that there are now no helper classes, so you can mitigate some of the eval() time by not doing the class creation + __init__. The results? val> python getattr.py Comparing 10000 iterations GetterSetter 0.236621975899 explicit function 0.20987200737 It's only about 10% slower, though for n==10 it's still about 8 times slower. And it has a nasty case of cyclical references (since __dict__ caches the lambda functions, which contain a reference to __dict__). I'll leave cleaning that up as an exercise to the student :) Andrew Dalke dalke at acm.org
- Previous message (by thread): Getters and Setters
- Next message (by thread): Getters and Setters
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
More information about the Python-list mailing list