| 1 |
from timeit import Timer |
|---|
| 2 |
|
|---|
| 3 |
# A control model |
|---|
| 4 |
setup_a = ''' |
|---|
| 5 |
class Model(object): |
|---|
| 6 |
pass |
|---|
| 7 |
m = Model() |
|---|
| 8 |
''' |
|---|
| 9 |
|
|---|
| 10 |
# A model which has a __setattr__ which does nothing |
|---|
| 11 |
setup_b = ''' |
|---|
| 12 |
class Model(object): |
|---|
| 13 |
def __setattr__(self, name, value): |
|---|
| 14 |
super(Model, self).__setattr__(name, value) |
|---|
| 15 |
m = Model() |
|---|
| 16 |
''' |
|---|
| 17 |
setup_b2 = ''' |
|---|
| 18 |
class Model(object): |
|---|
| 19 |
def __setattr__(self, name, value): |
|---|
| 20 |
self.__dict__[name] = value |
|---|
| 21 |
m = Model() |
|---|
| 22 |
''' |
|---|
| 23 |
|
|---|
| 24 |
# A model which has a __setattr__ which matches this patch |
|---|
| 25 |
setup_c = ''' |
|---|
| 26 |
class Model(object): |
|---|
| 27 |
def __setattr__(self, name, value): |
|---|
| 28 |
if name != '_modified_attrs' and (not hasattr(self, name) or |
|---|
| 29 |
value != getattr(self, name)): |
|---|
| 30 |
if hasattr(self, '_modified_attrs'): |
|---|
| 31 |
if name not in self._modified_attrs: |
|---|
| 32 |
self._modified_attrs.add(name) |
|---|
| 33 |
else: |
|---|
| 34 |
self._modified_attrs = set((name,)) |
|---|
| 35 |
super(Model, self).__setattr__(name, value) |
|---|
| 36 |
m = Model() |
|---|
| 37 |
''' |
|---|
| 38 |
|
|---|
| 39 |
# Optimized for speed, and dropped the value check as Brian suggested |
|---|
| 40 |
setup_d = ''' |
|---|
| 41 |
class Model(object): |
|---|
| 42 |
def __setattr__(self, name, value): |
|---|
| 43 |
try: |
|---|
| 44 |
if name not in self._modified_attrs: |
|---|
| 45 |
self._modified_attrs.add(name) |
|---|
| 46 |
except AttributeError: |
|---|
| 47 |
if name != '_modified_attrs': |
|---|
| 48 |
self._modified_attrs = set((name,)) |
|---|
| 49 |
super(Model, self).__setattr__(name, value) |
|---|
| 50 |
m = Model() |
|---|
| 51 |
''' |
|---|
| 52 |
|
|---|
| 53 |
# Hyper-optimized |
|---|
| 54 |
setup_h = ''' |
|---|
| 55 |
class Model(object): |
|---|
| 56 |
def __init__(self): |
|---|
| 57 |
self._reset_modified_attrs() |
|---|
| 58 |
def _reset_modified_attrs(self): |
|---|
| 59 |
self.__dict__['_modified_attrs'] = [] |
|---|
| 60 |
def __setattr__(self, name, value): |
|---|
| 61 |
if name not in self._modified_attrs: |
|---|
| 62 |
self._modified_attrs.append(name) |
|---|
| 63 |
self.__dict__[name] = value |
|---|
| 64 |
m = Model() |
|---|
| 65 |
''' |
|---|
| 66 |
|
|---|
| 67 |
# A model which has uses properties to track changes |
|---|
| 68 |
# Bonus: only field access would be tracked this way |
|---|
| 69 |
setup_f = ''' |
|---|
| 70 |
def track_property(klass, name): |
|---|
| 71 |
p = '_p_%s' % name |
|---|
| 72 |
def fget(o): |
|---|
| 73 |
getattr(o, p) |
|---|
| 74 |
def fset(o, value): |
|---|
| 75 |
set_modified(o) |
|---|
| 76 |
setattr(o, p, value) |
|---|
| 77 |
def set_modified(o): |
|---|
| 78 |
try: |
|---|
| 79 |
if name not in o._modified_attrs: |
|---|
| 80 |
o._modified_attrs.add(name) |
|---|
| 81 |
except AttributeError: |
|---|
| 82 |
o._modified_attrs = set((name,)) |
|---|
| 83 |
setattr(klass, name, property(fget, fset)) |
|---|
| 84 |
class Model(object): |
|---|
| 85 |
def __init__(self): |
|---|
| 86 |
track_property(self.__class__, 'an_attribute') |
|---|
| 87 |
track_property(self.__class__, 'another_attribute') |
|---|
| 88 |
m = Model() |
|---|
| 89 |
''' |
|---|
| 90 |
|
|---|
| 91 |
# A model which has uses a new style attribute object |
|---|
| 92 |
# Bonus: only field access would be tracked this way |
|---|
| 93 |
setup_g = ''' |
|---|
| 94 |
def track_property(klass, name): |
|---|
| 95 |
p = '_p_%s' % name |
|---|
| 96 |
def set_modified(o): |
|---|
| 97 |
try: |
|---|
| 98 |
if name not in o._modified_attrs: |
|---|
| 99 |
o._modified_attrs.add(name) |
|---|
| 100 |
except AttributeError: |
|---|
| 101 |
o._modified_attrs = set((name,)) |
|---|
| 102 |
class Attr(object): |
|---|
| 103 |
def __get__(self, instance, value): |
|---|
| 104 |
getattr(instance, p) |
|---|
| 105 |
def __set__(self, instance, value): |
|---|
| 106 |
set_modified(instance) |
|---|
| 107 |
setattr(instance, p, value) |
|---|
| 108 |
setattr(klass, name, Attr()) |
|---|
| 109 |
class Model(object): |
|---|
| 110 |
def __init__(self): |
|---|
| 111 |
track_property(self.__class__, 'an_attribute') |
|---|
| 112 |
track_property(self.__class__, 'another_attribute') |
|---|
| 113 |
m = Model() |
|---|
| 114 |
''' |
|---|
| 115 |
|
|---|
| 116 |
def timed(t, control=None): |
|---|
| 117 |
if control is None: |
|---|
| 118 |
return '%.3f' % (t) |
|---|
| 119 |
return '%.3f (%.1fx)' % (t, t/control) |
|---|
| 120 |
|
|---|
| 121 |
def do_test(test, extra_setup=''): |
|---|
| 122 |
control = Timer(test, setup_a+extra_setup).timeit() |
|---|
| 123 |
print "Control: ", timed(control) |
|---|
| 124 |
print "noop: ", timed(Timer(test, setup_b+extra_setup).timeit(), control) |
|---|
| 125 |
print "patch: ", timed(Timer(test, setup_c+extra_setup).timeit(), control) |
|---|
| 126 |
print "optimized:", timed(Timer(test, setup_d+extra_setup).timeit(), control) |
|---|
| 127 |
print "noop-h-op:", timed(Timer(test, setup_b2+extra_setup).timeit(), control) |
|---|
| 128 |
print "h-optimiz:", timed(Timer(test, setup_h+extra_setup).timeit(), control) |
|---|
| 129 |
print "property: ", timed(Timer(test, setup_f+extra_setup).timeit(), control) |
|---|
| 130 |
print "new-attr: ", timed(Timer(test, setup_g+extra_setup).timeit(), control) |
|---|
| 131 |
|
|---|
| 132 |
print "Single Assignment" |
|---|
| 133 |
do_test('m.an_attribute="test"') |
|---|
| 134 |
|
|---|
| 135 |
print |
|---|
| 136 |
print "Double Assignment" |
|---|
| 137 |
do_test('m.an_attribute="test";m.another_attribute="test"') |
|---|
| 138 |
|
|---|
| 139 |
print |
|---|
| 140 |
print "Retreiving" |
|---|
| 141 |
do_test('m.an_attribute', extra_setup='m.an_attribute="test"') |
|---|