__getattr__, __setattr__ and pickle
Bruno Desthuilliers
bdesth.quelquechose at free.quelquepart.fr
Tue Aug 12 18:35:09 EDT 2008
mwojc a écrit :
> Bruno Desthuilliers wrote:
(snip)
>> FWIW, you'd be better using a property instead of __getattr__ /
>> __setattr__ if possible.
>
> You're probably right again, in this case it's better to use property.
Since you seem to have concerns wrt/ execution time, properties might be
a little bit faster than __getattr__/__setattr__ hooks - cf below...
>> And while we're at it, you dont need to
>> manually take care of your index in the for loop - you can use
>> enumerate(iterable) instead:
>>
>> for j, net in enumerate(self.nets):
>> w1 = self.wmarks[j]
>> w2 = self.wmarks[j+1]
>> self._weights[w1:w2] = net.weights
>> return self._weights
>>
> Sometimes i use manual handling of index because i'm convinced that
> enumeration is a bit slower than this. But i'm not really sure about it...
It's easy to try out:
bruno at bibi ~ $ python
Python 2.5.1 (r251:54863, Apr 6 2008, 17:20:35)
[GCC 4.1.2 (Gentoo 4.1.2 p1.0.2)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> def manual(cnt):
... j = 0
... for i in xrange(cnt):
... x = i
... j += 1
...
>>> def auto(cnt):
... for j, i in enumerate(xrange(cnt)):
... x = i
...
>>> from timeit import Timer
>>> tm = Timer("manual(10000)", "from __main__ import manual")
>>> ta = Timer("auto(10000)", "from __main__ import auto")
>>> tm.timeit(1000)
3.3354489803314209
>>> tm.timeit(1000)
3.3376359939575195
>>> tm.timeit(1000)
3.3400180339813232
>>> ta.timeit(1000)
2.8350770473480225
>>> ta.timeit(1000)
2.8400650024414062
>>> ta.timeit(1000)
2.8361449241638184
>>>
Looks like enum is a bit faster by a mostly constant factor here.
And while we're at it:
>>> class Prop(object):
... @apply
... def prop():
... def fget(self): return self._prop
... def fset(self, val): self._prop = val
... return property(**locals())
... def __init__(self, val): self.prop=val
...
>>> class Hook(object):
... def __getattr__(self, name):
... if name == 'prop':
... return self._prop
... raise AttributeError("yadda")
... def __setattr__(self, name, val):
... if name == 'prop':
... self.__dict__['_prop'] = val
... else:
... # XXX : INCORRECT IMPLEMENTATION, DONT DO THIS !
... self.__dict__[name] = val
... # correct implementation:
... # super(Hook, self).__setattr__(name, value)
... def __init__(self, val): self.prop=val
...
>>> def testprop(cnt):
... p = Prop('test')
... for i in xrange(cnt):
... p.prop = i
... x = p.prop
...
>>> def testhook(cnt):
... h = Hook('test')
... for i in xrange(cnt):
... h.prop = i
... x = h.prop
...
>>> tp = Timer("testprop(1000)", "from __main__ import testprop")
>>> th = Timer("testhook(1000)", "from __main__ import testhook")
>>> tp.timeit(1000)
3.0640909671783447
>>> tp.timeit(1000)
3.0650019645690918
>>> th.timeit(1000)
7.0889511108398438
>>> th.timeit(1000)
7.0815410614013672
Looks like properties are significatively faster than the
__getattr__/__setattr__ hook too... Which is probably explained by the
following facts:
Looking for binding descriptors (like read/write properties) is the
first very stage of the attribute resolution algorithm (since they must
be looked up before the instance's __dict__ to avoid setting an instance
attribute which would then shadow the property).
OTHO, __getattr__ is always looked for last - which means it takes
longer to resolve __getattr__ than to resolve a read access to a
read/write property.
wrt/ __setattr__ - which is always called for attribute assignement,
whatever -, overloading it (instead of relying on the optimized builtin
implementation) is not only tricky (BTW, your implementation is broken -
try to add a read/write property to your class and enjoy...[1]), but
also cause a penalty for *all* attributes 'write' access.
[1] oh, yes, should I mention it ? The correct implementation is even
slower:
>>> class Hook(object):
... def __getattr__(self, name):
... if name == 'prop':
... return self._prop
... raise AttributeError("yadda")
... def __setattr__(self, name, val):
... if name == 'prop':
... self.__dict__['_prop'] = val
... else:
... #self.__dict__[name] = val
... super(Hook, self).__setattr__(name, value)
... def __init__(self, val): self.prop=val
...
>>> th.timeit(1000)
7.1943540573120117
>>> th.timeit(1000)
7.1930480003356934
>>>
HTH
More information about the Python-list
mailing list