[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

AltStyle によって変換されたページ (->オリジナル) /