dynamic, metaclasses and Virtual properties

Ernesto Revilla <[email protected]> Wed, 30 Jun 2004 18:21:53 +0200
Newsgroups gmane.comp.python.modeling
Message-ID <[email protected]>
Hi all,

I think that Python has a design error with properties, but it is easy 
to correct this. We can see that in the following example:

class Base(object):
    _name=None
    def getName(self):
        return self._name
    def setName(self, value):
        self._name=value
    name=property(getName,setName)

class Sub(Base):
    def setName(self, value):
       print "Setting name to '%s'" % value
       Base.setName(self,value)

Ok, here we can see how Sub extends the functionality of Base. But if we do:

instance=Sub()
instance.name="a name"

Sub.setName is NOT  called.!!!   Of course, it cand, because the 
property binds directly to the methods. Now we see the Base class 
corrected, using 'virtual' properties:

class Base(object):
    _name=None
    def getName(self):
        return self._name
    def setName(self, value):
        self._name=value
    name=property(lambda s: s.getName(), lambda s, v: s.setName(v))

class Sub(Base):
    def setName(self, value):
       print "Setting name to '%s'" % value
       Base.setName(self,value)

An try again:
 >>> instance=Sub()
 >>> instance.name="a name"
Setting name to 'a name'

In Modeling, when inheriting from class created with properties 
(dynamic), it does NOT the inherited methods. Is this reasonable? 

Here I have attached a little patch for dynamic.py. I made a local 
function mkProp, so that  variable binding is correct, otherwise  it 
does not work the way it should. Just a remark about the setter. As in 
the original code, a virtual setter is created only if the attribute is 
settable in the base class. In this case, we have two alternatives: 1. 
do not create a setter, as the original code, 2. create always a virtual 
setter and the setter in the base class raises a 
ReadOnlyAttributeException. (This is was I have done in a experimental 
framework.)

Best regards, Erny
VirtualProperties.patch (text/plain, 1.1 KB)
Index: dynamic.py
===================================================================
RCS file: /cvsroot/modeling/ProjectModeling/Modeling/dynamic.py,v
retrieving revision 1.1
diff -u -r1.1 dynamic.py
--- dynamic.py	16 Feb 2004 20:01:06 -0000	1.1
+++ dynamic.py	30 Jun 2004 16:07:47 -0000
@@ -144,10 +144,16 @@
 def add_properties(aClass, entity):
   for p in entity.classProperties():
     #print 'defining prop: ', p.name()
-    part_func_name=capitalizeFirstLetter(p.name())
-    prop=property(getattr(aClass, 'get'+part_func_name),
-                  getattr(aClass, 'set'+part_func_name), None)
-    setattr(aClass, p.name(), prop)
+    part_func_name=capitalizeFirstLetter(p.name())
+    readonly=not hasattr(aClass,'set'+part_func_name)
+    def mkProp(name, readonly=0):
+        getter=lambda s: getattr(s,'get'+name)()
+        if readonly:
+            setter=None
+        else:
+            setter=lambda s, v: getattr(s,'set'+name)(v)
+        return property(getter, setter)
+    setattr(aClass, p.name(), mkProp(part_func_name, readonly))
     
 def build(model, define_properties=0):
   module_name=model.packageName()