我设法在 abstractproperty 周围做了一个小包装器:
class partialAP(abc.abstractproperty):
def getter(self, func):
if getattr(func, '__isabstractmethod__', False) or getattr(self.fset, '__isabstractmethod__', False):
p = partialAP(func, self.fset)
else:
p = property(func, self.fset)
return p
def setter(self, func):
if getattr(self.fget, '__isabstractmethod__', False) or getattr(func, '__isabstractmethod__', False):
p = partialAP(self.fget, func)
else:
p = property(self.fset, func)
return p
然后您的代码只需稍作修改即可工作:
class _mystring(object):
__metaclass__ = abc.ABCMeta
@partialAP
@abc.abstractmethod
def str(self):
pass
@str.setter
def str(self,value):
self._str = value
class uppercase(_mystring):
@_mystring.str.getter
def str(self):
return self._str.upper()
然后行为如愿:
>>> _mystring()
Traceback (most recent call last):
File "<pyshell#3>", line 1, in <module>
_mystring()
TypeError: Can't instantiate abstract class _mystring with abstract methods str
>>> u = uppercase()
>>> u.str = 'a'
>>> u.str
'A'
我的partialAP 包装器必须与abc.abstractmethod 结合使用。您所做的是使用abstractmethod 来装饰您想要抽象的属性(getter 或setter)的“部分”,并使用partialAP 作为第二个装饰器。 partialAP 定义了 getter/setter 函数,只有当 getter 和 setter 都是非抽象的时,才会用普通属性替换属性。
显然,您必须稍微扩展一下才能使其也值得使用属性删除器。如果您有更复杂的继承层次结构,也可能存在无法正确处理的极端情况。
后代的旧解决方案:
class _mystring(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def _strGet(self):
pass
def _strSet(self,value):
self._str = value
str = property(_strGet, _strSet)
class uppercase(_mystring):
def _strGet(self):
return self._str.upper()
str = _mystring.str.getter(_strGet)
然后就可以了:
>>> _mystring()
Traceback (most recent call last):
File "<pyshell#39>", line 1, in <module>
_mystring()
TypeError: Can't instantiate abstract class _mystring with abstract methods _strGet
>>> u = uppercase()
>>> u.str = 'a'
>>> u.str
'A'
诀窍是您不必使用方便的装饰器。 abstractproperty 装饰器将整个属性标记为抽象。您要做的是创建两个方法,一个抽象的(用于 getter)和一个常规的(用于 setter),然后创建一个组合它们的常规属性。然后,当您扩展类时,您必须重写抽象 getter 并将其与基类属性显式“混合”(使用 _mystring.str.getter)。
您不能使用@_mystring.str.getter 的原因是装饰器强制getter 和setter 与属性本身具有相同的名称。如果您只想将两者中的一个标记为摘要,则它们必须具有不同的名称,否则您无法分别获得它们。
请注意,此解决方案要求子类为其 getter 赋予正确的名称(在本例中为 _strGet),以便满足他们已覆盖抽象方法的 ABC。