我正在尝试使用属性来实现抽象类,但我无法简单地定义它。
我只想定义属性名称以约束子类拥有它,但是我不想在继承我的抽象类的每个类中复制/粘贴getter和setter。 这是我找到的解决方案,但我认为不是很好:
class AbstractC(ABC):
@property
@abstractmethod
def a(self):
pass
class ConcreteC1(AbstractC):
def __init__(self, name):
self.a = name
def a(self):
pass
class ConcreteC2(AbstractC):
def __init__(self, name):
self.a = name
class ConcreteC3(AbstractC):
def __init__(self, name):
self.poney = name
ConcreteC1('foobar') # ok
ConcreteC2('foobar') # error !
ConcreteC3('foobar') # error !
class AbstractC(ABC):
@property
@abstractmethod
def a(self):
pass
class ConcreteC1(AbstractC):
a = None
def __init__(self, name):
self.a = name
class ConcreteC2(AbstractC):
def __init__(self, name):
self.a = name
class ConcreteC3(AbstractC):
def __init__(self, name):
self.poney = name
ConcreteC1('foobar') # ok
ConcreteC2('foobar') # error !
ConcreteC3('foobar') # error !
class AbstractC(ABC):
@abstractmethod
def __init__(self, val):
self.a = val
class ConcreteC1(AbstractC):
def __init__(self, name):
self.a = name
class ConcreteC2(AbstractC):
def __init__(self, name):
self.poney = name
ConcreteC1('foobar') # ok
ConcreteC2('foobar') # no error !
那么有没有一种方法可以获取具有抽象属性的优雅,健壮和紧凑的抽象类?还是我试图得到一些不可能的东西?我正在考虑接近的东西:
class AbstractC(ABC):
@property
@abstractmethod
def a(self):
pass
class ConcreteC(AbstractC):
def __init__(self, name):
self.a = name
如果没有这样的解决方案,最好的解决方案是什么?
答案 0 :(得分:2)
您可能会误用mongodump
进行花式继承
namedtuples
但是,恕我直言,如果您提出from collections import namedtuple
BaseAttributes = namedtuple('base', ['attr1', 'attr2'])
print(BaseAttributes('one', 2))
class SomethingElse(BaseAttributes):
def method(self):
return 3
blubb = SomethingElse('A', 5)
blubb.method()
,例如,您的最后一个建议是有意义的:
NotImplementedError
答案 1 :(得分:1)
也许这会有所帮助。我做了一个继承自ABC
的类。它定义了在创建新的子类之后调用的方法__init_subclass__
。它执行下一个操作:
对于声明的每个抽象属性,在子类中搜索相同的方法。如果存在(它是一个函数对象),则将其转换为属性,并将其替换为子类字典。
from abc import ABC, abstractmethod
class Foo(ABC):
def __init_subclass__(cls):
super().__init_subclass__()
###### This is the new part. I explain it at the end of the answer
for name, value in attrs.items():
if name not in cls.__dict__:
setattr(cls, name, property(lambda *args, **kwargs: value))
######
# Iterate throught all abstract methods on the class
for name in Foo.__abstractmethods__:
absmethod = Foo.__dict__[name]
# Check if the abstract method is a property
if not isinstance(absmethod, property):
continue
# Check if there is a method defined in the subclass with the same name
if name not in cls.__dict__ or not callable(cls.__dict__[name]):
continue
method = cls.__dict__[name]
# If the method is not already a property, we decorate it automatically...
if not isinstance(method, property):
setattr(cls, name, property(method))
@property
@abstractmethod
def a(self):
return 1
现在定义一个子类并对其进行测试:
class Bar(Foo):
def __init__(self):
pass
def a(self):
return 2
@property
def b(self):
return 3
obj = Bar()
print(obj.a)
print(obj.b)
输出将是:
2
3
下一个代码将引发错误,因为并非所有抽象方法都已实现:
class Qux(Foo):
pass
编辑: 现在您也可以这样做:
class Bar(Foo, a=1):
pass
print(Bar().a) # 1
答案 2 :(得分:1)
仍然存在问题。如果我选择引发错误的实现,则必须向该方法中添加@media (max-width: 600px) {
.toolbarbutton a {
text-decoration: none;
}
}
或我可以调用ConcreteC()。a,即使未设置a也不会引发错误:
@property
但是,如果我添加class AbstractC(ABC):
def a(self):
raise NotImplementedError('Implement _a_ method')
class ConcreteC(AbstractC):
def __init__(self, val):
super().__init__()
self.poney = val
In [3]: ConcreteC('foobar').a
Out[3]: <bound method AbstractC.a of <__main__.ConcreteC object at 0x7f2e1c6b0518>>
则会收到错误消息:
@property
编辑:
这是我选择的解决方案:
class AbstractC(ABC):
@property
def a(self):
raise NotImplementedError('Implement _a_ method')
class ConcreteC(AbstractC):
def __init__(self, val):
super().__init__()
self.a = val
In [4]: ConcreteC('foobar')
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-55-587237cb76e5> in <module>
----> 1 ConcreteC('foobar')
~/workspace/draft.py in __init__(self, val)
151 def __init__(self, val):
152 super().__init__()
--> 153 self.a = val
154
155
AttributeError: can't set attribute
这样,我可以非常简单地编辑“ a”,如果没有定义,则会在get上引发异常。我不知道要使设置器工作,它必须与属性名称相同。 最后,我想要的不是抽象属性,而是抽象类中的具体属性。
class AbstractC(ABC):
@property
def a(self):
try:
return self._a
except AttributeError:
raise NotImplementedError('Implement _a_ method')
@a.setter
def a(self, val):
self._a = val
class ConcreteC(AbstractC):
def __init__(self, val):
self.a = val