我想要一个抽象类,该抽象类强制每个派生类在其__init__
方法中设置某些属性。
我查看了几个不能完全解决问题的问题,特别是here或here。 This看起来很有希望,但我无法使其正常工作。
我认为我想要的结果可能类似于以下伪代码:
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
以下是我尝试过的一些事情:
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
然后我尝试创建一个实例:
>>> 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
>>>
哪个告诉我实现这些方法,但是我以为我说这些是properties
?
我当前的解决方法有一个缺陷,就是__init__
方法可以在派生类中被覆盖,但是就目前而言,这至少确保了(对我而言)我始终知道所请求的属性已设置: / p>
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
但是问题是
>>> 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'
这如何正确实施?
答案 0 :(得分:6)
值得注意的是,自定义元类经常被皱眉,但是您可以用一个人解决这个问题。
Here是一篇很好的文章,讨论了它们如何工作以及何时有用。此处的解决方案实质上是在调用Transform.scale(
scale: 0.5,
child: IconButton(
onPressed: (){},
icon: new Image.asset("images/IG.png"),
),
),
之后对所需的属性进行检查。
__init__
下面是我的原始答案,它更全面地探讨了该主题。
我认为部分原因是将 instance属性与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')
装饰器包装的对象混淆了。
一个不引入抽象类的小例子是
property
根据Python docs,由于3.3,>>> 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'
是多余的,实际上反映了您尝试的解决方案。
该解决方案的问题在于,您的子类没有实现具体的属性,而是仅使用实例属性覆盖它。
为了继续使用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
的子类。
我的建议是不要使Quadrature
或xyz
抽象,而是处理是否在运行时设置它们,即捕获访问值时可能弹出的weights
。>
答案 1 :(得分:2)
为了强制子类实现属性或方法,如果未实现此方法,则需要引发错误:
from abc import ABCMeta, abstractmethod, abstractproperty
class Quadrature(object, metaclass=ABCMeta):
@abstractproperty
def xyz(self):
raise NotImplementedError
答案 2 :(得分:2)
这可能是由于python 3.7的更改(我希望您正在使用-因为它很酷!),因为它添加了type hinting
和添加类annotations的功能,这些功能是为dataclasses
。我认为,它与您最初想要的语法非常接近。您想要的超类将如下所示:
from abc import ABC, abstractmethod
from typing import List
class PropertyEnfocedABC(ABC):
def __init__(self):
annotations = self.__class__.__dict__.get('__annotations__', {})
for name, type_ in annotations.items():
if not hasattr(self, name):
raise AttributeError(f'required attribute {name} not present '
f'in {self.__class__}')
现在,要观看它的运行情况。
class Quadratic(PropertyEnfocedABC):
xyz: int
weights: List[int]
def __init__(self):
self.xyz = 2
self.weights = [4]
super().__init__()
或更准确地说,结合抽象方法和属性:
class Quadrature(PropertyEnforcedABC):
xyz: int
weights: int
@abstractmethod
def __init__(self, order):
pass
@abstractmethod
def some_stupid_function(self, n):
return self.xyz + self.weights + n
现在,PropertyEnforcedABC
的子类的任何子类都必须设置在该类中被注释的属性(如果您不为注释提供类型,它将不被视为注释),因此,如果二次方的构造函数未设置xyz
或weights
,将引发属性错误。请注意,您必须在init的末尾调用构造函数,但这不是真正的问题,如果您真的不喜欢,可以通过在上述代码周围包装自己的元类来解决此问题。
您可以根据需要修改PropertyEnforcedABC
(例如,强制设置属性的类型)等等。您甚至可以检查Optional
并忽略它们。