k1n*_*ext 14 python oop abstract-class
我想有一个抽象类,它强制每个派生类在其__init__方法中设置某些属性。
我已经看了几个不能完全解决我问题的问题,特别是在这里或这里。这看起来很有希望,但我无法使其正常运行。
我假设我想要的结果可能类似于以下伪代码:
from abc import ABCMeta, abstractmethod
class Quadrature(object, metaclass=ABCMeta):
@someMagicKeyword #<==== This is what I want, but can't get working
xyz
@someMagicKeyword #<==== This is what I want, but can't get working
weights
@abstractmethod
def __init__(self, order):
pass
def someStupidFunctionDefinedHere(self, n):
return self.xyz+self.weights+n
class QuadratureWhichWorks(Quadrature):
# This shall work because we initialize xyz and weights in __init__
def __init__(self,order):
self.xyz = 123
self.weights = 456
class QuadratureWhichShallNotWork(Quadrature):
# Does not initialize self.weights
def __init__(self,order):
self.xyz = 123
Run Code Online (Sandbox Code Playgroud)
这是我尝试过的一些方法:
from abc import ABCMeta, abstractmethod
class Quadrature(object, metaclass=ABCMeta):
@property
@abstractmethod
def xyz(self):
pass
@property
@abstractmethod
def weights(self):
pass
@abstractmethod
def __init__(self, order):
pass
def someStupidFunctionDefinedHere(self, n):
return self.xyz+self.weights+n
class QuadratureWhichWorks(Quadrature):
# This shall work because we initialize xyz and weights in __init__
def __init__(self,order):
self.xyz = 123
self.weights = 456
Run Code Online (Sandbox Code Playgroud)
然后,我尝试创建一个实例:
>>> from example1 import *
>>> Q = QuadratureWhichWorks(10)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class QuadratureWhichWorks with abstract methods weights, xyz
>>>
Run Code Online (Sandbox Code Playgroud)
哪个告诉我实现这些方法,但是我以为我说这些是properties?
我当前的解决方法有一个缺陷,即该__init__方法可以在派生类中被覆盖,但是就目前而言,这至少可以确保(对我而言)我始终知道所请求的属性已设置:
from abc import ABCMeta, abstractmethod
class Quadrature(object, metaclass=ABCMeta):
@abstractmethod
def computexyz(self,order):
pass
@abstractmethod
def computeweights(self,order):
pass
def __init__(self, order):
self.xyz = self.computexyz(order)
self.weights = self.computeweights(order)
def someStupidFunctionDefinedHere(self, n):
return self.xyz+self.weights+n
class QuadratureWhichWorks(Quadrature):
def computexyz(self,order):
return order*123
def computeweights(self,order):
return order*456
class HereComesTheProblem(Quadrature):
def __init__(self,order):
self.xyz = 123
# but nothing is done with weights
def computexyz(self,order):
return order*123
def computeweights(self,order): # will not be used
return order*456
Run Code Online (Sandbox Code Playgroud)
但是问题是
>>> from example2 import *
>>> Q = HereComesTheProblem(10)
>>> Q.xyz
123
>>> Q.weights
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'HereComesTheProblem' object has no attribute 'weights'
Run Code Online (Sandbox Code Playgroud)
如何正确实施?
值得注意的是,自定义元类通常不被接受,但是您可以用一个人解决这个问题。
这是一篇很好的文章,讨论了它们如何工作以及何时有用。实质上,解决方案是在__init__调用之后对所需的属性进行检查。
from abc import ABCMeta, abstractmethod
# our version of ABCMeta with required attributes
class MyMeta(ABCMeta):
required_attributes = []
def __call__(self, *args, **kwargs):
obj = super(MyMeta, self).__call__(*args, **kwargs)
for attr_name in obj.required_attributes:
if not getattr(obj, attr_name):
raise ValueError('required attribute (%s) not set' % attr_name)
return obj
# similar to the above example, but inheriting MyMeta now
class Quadrature(object, metaclass=MyMeta):
required_attributes = ['xyz', 'weights']
@abstractmethod
def __init__(self, order):
pass
class QuadratureWhichWorks(Quadrature):
# This shall work because we initialize xyz and weights in __init__
def __init__(self,order):
self.xyz = 123
self.weights = 456
q = QuadratureWhichWorks('foo')
class QuadratureWhichShallNotWork(Quadrature):
def __init__(self, order):
self.xyz = 123
q2 = QuadratureWhichShallNotWork('bar')
Run Code Online (Sandbox Code Playgroud)
以下是我的原始答案,它更全面地探讨了该主题。
我认为其中某些原因是混淆了实例属性和property装饰器包装的对象。
一个不引入抽象类的小例子是
>>> class Joker(object):
>>> # a class attribute
>>> setup = 'Wenn ist das Nunstück git und Slotermeyer?'
>>>
>>> # a read-only property
>>> @property
>>> def warning(self):
>>> return 'Joke Warfare is explicitly banned bythe Geneva Conventions'
>>>
>>> def __init__(self):
>>> self.punchline = 'Ja! Beiherhund das Oder die Flipperwaldt gersput!'
>>> j = Joker()
>>> # we can access the class attribute via class or instance
>>> Joker.setup == j.setup
>>> # we can get the property but cannot set it
>>> j.warning
'Joke Warfare is explicitly banned bythe Geneva Conventions'
>>> j.warning = 'Totally safe joke...'
AttributeError: cant set attribute
>>> # instance attribute set in __init__ is only accessible to that instance
>>> j.punchline != Joker.punchline
AttributeError: type object 'Joker' has no attribute 'punchline'
Run Code Online (Sandbox Code Playgroud)
根据Python文档,从3.3开始,它abstractproperty是多余的,实际上反映了您尝试的解决方案。该解决方案的问题在于,您的子类没有实现具体的属性,而是仅使用实例属性覆盖它。为了继续使用该abc软件包,您可以通过实现这些属性来处理,即
>>> from abc import ABCMeta, abstractmethod
>>> class Quadrature(object, metaclass=ABCMeta):
>>>
>>> @property
>>> @abstractmethod
>>> def xyz(self):
>>> pass
>>>
>>> @property
>>> @abstractmethod
>>> def weights(self):
>>> pass
>>>
>>> @abstractmethod
>>> def __init__(self, order):
>>> pass
>>>
>>> def someStupidFunctionDefinedHere(self, n):
>>> return self.xyz+self.weights+n
>>>
>>>
>>> class QuadratureWhichWorks(Quadrature):
>>> # This shall work because we initialize xyz and weights in __init__
>>> def __init__(self,order):
>>> self._xyz = 123
>>> self._weights = 456
>>>
>>> @property
>>> def xyz(self):
>>> return self._xyz
>>>
>>> @property
>>> def weights(self):
>>> return self._weights
>>>
>>> q = QuadratureWhichWorks('foo')
>>> q.xyz
123
>>> q.weights
456
Run Code Online (Sandbox Code Playgroud)
我认为这虽然有些笨拙,但实际上取决于您打算如何实现的子类Quadrature。我的建议是不要做xyz或weights抽象的,而是处理他们是否在运行时设置的,即捕获任何AttributeError访问该值时可能会弹出秒。