from timeit import Timer

# A control model
setup_a = '''
class Model(object):
    pass
m = Model()
'''

# A model which has a __setattr__ which does nothing
setup_b = '''
class Model(object):
    def __setattr__(self, name, value):
        super(Model, self).__setattr__(name, value)
m = Model()
'''

# A model which has a __setattr__ which matches this patch
setup_c = '''
class Model(object):
    def __setattr__(self, name, value):
        if name != '_modified_attrs' and (not hasattr(self, name) or
                                          value != getattr(self, name)):
            if hasattr(self, '_modified_attrs'):
                if name not in self._modified_attrs: 
                    self._modified_attrs.add(name)
            else:
                self._modified_attrs = set((name,))
        super(Model, self).__setattr__(name, value)
m = Model()
'''

# Optimized for speed, and dropped the value check as Brian suggested
setup_d = '''
class Model(object):
    def __setattr__(self, name, value):
        try:
            if name not in self._modified_attrs: 
                self._modified_attrs.add(name)
        except AttributeError:
            if name != '_modified_attrs':
                self._modified_attrs = set((name,))
        super(Model, self).__setattr__(name, value)
m = Model()
'''

setup_a1 = setup_a + '''
m.another_attribute="test"
'''

setup_b1 = setup_b + '''
m.another_attribute="test"
'''

setup_c1 = setup_c + '''
m.another_attribute="test"
'''

setup_d1 = setup_d + '''
m.another_attribute="test"
'''

print "Single Assignment"
print "Control:  ", Timer('m.an_attribute="test"', setup_a).timeit()
print "noop:     ", Timer('m.an_attribute="test"', setup_b).timeit()
print "patch:    ", Timer('m.an_attribute="test"', setup_c).timeit()
print "optimized:", Timer('m.an_attribute="test"', setup_d).timeit()
print
print "Double Assignment"
print "Control:  ", Timer('m.an_attribute="test"', setup_a1).timeit()
print "Noop:     ", Timer('m.an_attribute="test"', setup_b1).timeit()
print "Patch:    ", Timer('m.an_attribute="test"', setup_c1).timeit()
print "Optimized:", Timer('m.an_attribute="test"', setup_d1).timeit()
