Sometimes you define properties in base class and override the getter setter methods in derived classes. Then you find out the base class though has derived properties are still pointing to baseclasse's methods not the overriden ones.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | ########## Solution I (Better) ##########
# Inspired by many other recipes in this cookbook.
class lazyproperty(object):
def __init__(self, fget=None, fset=None, fdel=None, doc=''):
for attr in ('fget', 'fset'):
func = locals()[attr]
if callable(func):
setattr(self, attr, func.func_name)
setattr(self, '__doc__', doc)
def __get__(self, obj=None, type=None):
if not obj:
return 'property'
if self.fget:
return getattr(obj, self.fget)()
def __set__(self, obj, arg):
if self.fset:
return getattr(obj, self.fset)(arg)
############ TEST ################
if __name__ == '__main__':
class A(object):
def __init__(self):
self._p = 1
def _g(self):
print 'inside A._g'
return self._p
def _s(self, p):
print 'inside A._s'
self._p = p
p = property (_g, _s, None, 'p doc')
lp = lazyproperty (_g, _s, None, 'p doc')
class B(A):
def _g(self):
print '** inside B._g **'
return self._p
b = B()
print 'property in action'
print ' ', b.p
print 'lazyproperty in action'
print ' ', b.lp
b.p = 3
########## Solution II (Old one) #########
def rebind(cls):
props = [attr for attr in dir(cls) if type(getattr(cls, attr)) == property]
for prop_name in props:
prop = getattr(cls, prop_name)
getter_name = setter_name = destroyer_name = 'xxxxxxxx'
if prop.fget:
getter_name = prop.fget.__name__
if prop.fset:
setter_name = prop.fset.__name__
if prop.fdel:
destroyer_name = prop.fdel.__name__
p_doc = prop.__doc__
getter = getattr(cls, getter_name, None)
setter = getattr(cls, setter_name, None)
destroyer = getattr(cls, destroyer_name, None)
setattr(cls, prop_name, property(getter, setter, destroyer, p_doc))
############ TEST ################
if __name__ == '__main__':
class A(object):
def __init__(self):
self._p = 1
def _g(self):
print 'inside A._g'
return self._p
def _s(self, p):
print 'inside A._s'
self._p = p
p = property(_g, _s, None, 'p doc')
class B(A):
def _g(self):
print '** inside B._g **'
return self._p
b = B()
print b.p
b.p = 3
rebind(B)
b = B()
print b.p
b.p = 3
|
I have proposed two solution to solve the problem mentioned in the Description. The second solution is the one I proposed earlier however has some limitations as below 1. Modifies the default property behavior. May be you want it that way but might surprise others. 2. Can not handle if lambda is used as fget.
First solution is the latest and much better IMO. It hardly requires any explanation.
another option... paste.util.classinit.build_properties provides something similar:
http://svn.pythonpaste.org/Paste/trunk/paste/util/classinit.py
It uses a convention of __get, __set, __del suffixes to functions to build properties (automatically), and also to rewrite properties in a similar fashion when subclassing. Generally you'd run it in __classinit__ (using the metaclass also in that module).
similar but different. add_getters / add_setters / add_getters_and_setters in dulcinea.spec has a simpler job - look for "specifications in a class, in the format
And you'll end up with a class that has set/get_name and set/get_age methods with appropriate checks for input in the set methods against the specification. The utility function intentionally does not override existing methods - often I need to write a custom "setter" that does more than just check for correct input. Also, "spec" is persistence method agnostic, although I now do most of my work against a Durus object database, I could see using this package with SQL Object or other approaches.
Tasty: http://www.mems-exchange.org/software/dulcinea/Dulcinea-0.10.tar.gz/Dulcinea-0.10/lib/spec.py
Part of a larger package: http://www.mems-exchange.org/software/dulcinea/