[Python-ideas] Would it possible to define abstract read/write properties with decorators?
Darren Dale
dsdale24 at gmail.com
Sat Mar 19 16:29:23 CET 2011
On Fri, Mar 18, 2011 at 2:36 PM, Guido van Rossum <guido at python.org> wrote:
> On Fri, Mar 18, 2011 at 10:29 AM, Darren Dale <dsdale24 at gmail.com> wrote:
>> On Sun, Mar 13, 2011 at 12:49 PM, Darren Dale <dsdale24 at gmail.com> wrote:
>>> On Sun, Mar 13, 2011 at 11:18 AM, Darren Dale <dsdale24 at gmail.com> wrote:
>>> [...]
>>>> It seems like it should be possible for Python to support the
>>>> decorator syntax for declaring abstract read/write properties. The
>>>> most elegant approach might be the following, if it could be
>>>> supported:
>>>>
>>>> class Foo(metaclass=ABCMeta):
>>>> # Note the use of @property rather than @abstractproperty:
>>>> @property
>>>> @abstractmethod
>>>> def bar(self):
>>>> return 1
>>>> @bar.setter
>>>> @abstractmethod
>>>> def bar(self, val):
>>>> pass
[...]
>> The modifications to "property" to better support abstract base
>> classes using the decorator syntax and @abstractmethod (rather than
>> @abstractproperty) are even simpler than I originally thought:
>>
>> class Property(property):
>>
>> def __init__(self, *args, **kwargs):
>> super(Property, self).__init__(*args, **kwargs)
>> for f in (self.fget, self.fset, self.fdel):
>> if getattr(f, '__isabstractmethod__', False):
>> self.__isabstractmethod__ = True
>> break
>>
>>>
>>> class C(metaclass=abc.ABCMeta):
>>> @Property
>>> @abc.abstractmethod
>>> def x(self):
>>> return 1
>>> @x.setter
>>> @abc.abstractmethod
>>> def x(self, val):
>>> pass
>>>
>>> try:
>>> c=C()
>>> except TypeError as e:
>>> print(e)
>>>
>>> class D(C):
>>> @C.x.getter
>>> def x(self):
>>> return 2
>>>
>>> try:
>>> d=D()
>>> except TypeError as e:
>>> print(e)
>>>
>>> class E(D):
>>> @D.x.setter
>>> def x(self, val):
>>> pass
>>>
>>> print(E())
>>>
>>
>> running this example yields:
>>
>> Can't instantiate abstract class C with abstract methods x
>> Can't instantiate abstract class D with abstract methods x
>> <__main__.E object at 0x212ee10>
>>
>> Wouldn't it be possible to include this in python-3.3?
>
> Sounds good to me.
I took a stab at this, but unfortunately I have not been able to
perform a complete build of python from the mercurial checkout on
either ubuntu 11.04 or OS X 10.6.6, for reasons that appear unrelated
to the changes below (undefined setlocale symbols on OS X, Could not
find platform dependent libraries <exec_prefix> segfault on ubuntu).
I'm an experienced python programmer, but not an experienced python
hacker. Would anyone care to comment on (or test) the changes?:
diff -r e34b09c69dd3 Objects/descrobject.c
--- a/Objects/descrobject.c Sat Mar 12 22:31:06 2011 -0500
+++ b/Objects/descrobject.c Sat Mar 19 11:22:14 2011 -0400
@@ -1117,6 +1121,7 @@
PyObject *prop_set;
PyObject *prop_del;
PyObject *prop_doc;
+ PyObject *prop_isabstract;
int getter_doc;
} propertyobject;
@@ -1128,6 +1133,8 @@
{"fset", T_OBJECT, offsetof(propertyobject, prop_set), READONLY},
{"fdel", T_OBJECT, offsetof(propertyobject, prop_del), READONLY},
{"__doc__", T_OBJECT, offsetof(propertyobject, prop_doc), READONLY},
+ {"__isabstractmethod__", T_OBJECT,
+ offsetof(propertyobject, prop_isabstract), READONLY},
{0}
};
@@ -1180,6 +1187,7 @@
Py_XDECREF(gs->prop_set);
Py_XDECREF(gs->prop_del);
Py_XDECREF(gs->prop_doc);
+ Py_XDECREF(gs->prop_isabstract);
self->ob_type->tp_free(self);
}
@@ -1213,7 +1221,7 @@
PyErr_SetString(PyExc_AttributeError,
value == NULL ?
"can't delete attribute" :
- "can't set attribute");
+ "can't set attribute");
return -1;
}
if (value == NULL)
@@ -1263,6 +1271,21 @@
return new;
}
+static void
+property_identify_abstract_method(PyObject *self, PyObject *method)
+{
+ /* Set self.__isabstractmethod__ if method is abstract */
+ if (method != NULL){
+ PyObject *is_abstract = PyObject_GetAttrString(method,
+ "__isabstractmethod__");
+ if (PyObject_IsTrue(is_abstract) > 0){
+ Py_INCREF(Py_True);
+ PyObject_SetAttrString(self, "__isabstractmethod__", Py_True);
+ }
+ Py_DECREF(is_abstract);
+ }
+}
+
static int
property_init(PyObject *self, PyObject *args, PyObject *kwds)
{
@@ -1285,11 +1308,13 @@
Py_XINCREF(set);
Py_XINCREF(del);
Py_XINCREF(doc);
+ Py_INCREF(Py_False);
prop->prop_get = get;
prop->prop_set = set;
prop->prop_del = del;
prop->prop_doc = doc;
+ prop->prop_isabstract = Py_False;
prop->getter_doc = 0;
/* if no docstring given and the getter has one, use that one */
@@ -1320,6 +1345,11 @@
}
}
+ /* set __isabstractmethod__ if fget, fset, or fdel are abstract methods */
+ property_identify_abstract_method(self, get);
+ property_identify_abstract_method(self, set);
+ property_identify_abstract_method(self, del);
+
return 0;
}
More information about the Python-ideas
mailing list