Welcome, guest | Sign In | My Account | Store | Cart

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.

Python, 92 lines
 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.

2 comments

Ian Bicking 16 years, 2 months ago  # | flag

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).

Michael Watkins 16 years, 2 months ago  # | flag

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

class Foo:
    name_is = str
    age_is = either(int, long)

add_getters_and_setters(Foo)

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/

Created by Shekhar Tiwatne on Mon, 3 Oct 2005 (PSF)
Python recipes (4591)
Shekhar Tiwatne's recipes (6)

Required Modules

  • (none specified)

Other Information and Tasks