如何创建抽象 class 属性(可能是只读的)
How to create an abstract class attribute (potentially read-only)
我花了很多时间研究这个问题,但 none 的答案似乎符合我的意愿。
我有一个带有 class 属性的抽象 class 我希望每个子 class 都被强制实现
class AbstractFoo():
forceThis = 0
所以当我这样做时
class RealFoo(AbstractFoo):
pass
它抛出一个错误告诉我在我实现 forceThis
.
之前它无法创建 class
我该怎么做?
(我不希望该属性是只读的,但如果这是唯一的解决方案,我会接受。)
对于 class 方法,我发现我可以做到
from abc import ABCMeta, abstractmethod
class AbstractFoo(metaclass=ABCMeta):
@classmethod
@abstractmethod
def forceThis():
"""This must be implemented"""
所以
class RealFoo(AbstractFoo):
pass
至少会抛出错误 TypeError: Can't instantiate abstract class EZ with abstract methods forceThis
(虽然它不会强制 forceThis
成为 class 方法。)
如何让 class 属性弹出类似的错误消息?
您可以通过定义自己的元类来做到这一点。类似于:
class ForceMeta(type):
required = ['foo', 'bar']
def __new__(mcls, name, bases, namespace):
cls = super().__new__(mcls, name, bases, namespace)
for prop in mcls.required:
if not hasattr(cls, prop):
raise NotImplementedError('must define {}'.format(prop))
return cls
现在您可以将其用作您自己的元类类:
class RequiredClass(metaclass=ForceMeta):
foo = 1
这将引发错误 'must define bar'。
虽然您可以使用 metaclass 做一些非常相似的事情,如@Daniel Roseman 的 中所示,但也可以使用 class 装饰器来完成。它们有几个优点是当定义 class 时会发生错误,而不是在创建一个实例时发生错误,并且在 Python 2 和 3 中指定它们的语法是相同的. 有些人也觉得它们更简单易懂
def check_reqs(cls):
requirements = 'must_have',
missing = [req for req in requirements if not hasattr(cls, req)]
if missing:
raise NotImplementedError(
'class {} did not define required attribute{} named {}'.format(
cls.__name__, 's' if len(missing) > 1 else '',
', '.join('"{}"'.format(name) for name in missing)))
return cls
@check_reqs
class Foo(object): # OK
must_have = 42
@check_reqs
class Bar(object): # raises a NotImplementedError
pass
我根据之前发布的内容想出了一个解决方案。 (谢谢@Daniel Roseman 和@martineau)
我创建了一个名为 ABCAMeta 的元class(最后一个 'A' 代表 'Attributes')。
class有两种工作方式。
A class 仅使用 ABCAMeta 作为元class 必须有一个名为 required_attributes
的 属性 应包含名称列表您想要在 class
的未来子 class 上需要的所有属性
父级元class为ABCAMeta的class必须具有其父级class(es)指定的所有必需属性。
例如:
class AbstractFoo(metaclass=ABCAMeta):
required_attributes = ['force_this']
class RealFoo(AbstractFoo):
pass
会抛出错误:
NameError: Class 'RealFoo' has not implemented the following attributes: 'force_this'
正是我想要的。
from abc import ABCMeta
class NoRequirements(RuntimeError):
def __init__(self, message):
RuntimeError.__init__(self, message)
class ABCAMeta(ABCMeta):
def __init__(mcls, name, bases, namespace):
ABCMeta.__init__(mcls, name, bases, namespace)
def __new__(mcls, name, bases, namespace):
def get_requirements(c):
"""c is a class that should have a 'required_attributes' attribute
this function will get that list of required attributes or
raise a NoRequirements error if it doesn't find one.
"""
if hasattr(c, 'required_attributes'):
return c.required_attributes
else:
raise NoRequirements(f"Class '{c.__name__}' has no 'required_attributes' property")
cls = super().__new__(mcls, name, bases, namespace)
# true if no parents of the class being created have ABCAMeta as their metaclass
basic_metaclass = True
# list of attributes the class being created must implement
# should stay empty if basic_metaclass stays True
reqs = []
for parent in bases:
parent_meta = type(parent)
if parent_meta==ABCAMeta:
# the class being created has a parent whose metaclass is ABCAMeta
# the class being created must contain the requirements of the parent class
basic_metaclass=False
try:
reqs.extend(get_requirements(parent))
except NoRequirements:
raise
# will force subclasses of the created class to define
# the attributes listed in the required_attributes attribute of the created class
if basic_metaclass:
get_requirements(cls) # just want it to raise an error if it doesn't have the attributes
else:
missingreqs = []
for req in reqs:
if not hasattr(cls, req):
missingreqs.append(req)
if len(missingreqs)!=0:
raise NameError(f"Class '{cls.__name__}' has not implemented the following attributes: {str(missingreqs)[1:-1]}")
return cls
欢迎在评论中提出任何改进建议。
我花了很多时间研究这个问题,但 none 的答案似乎符合我的意愿。
我有一个带有 class 属性的抽象 class 我希望每个子 class 都被强制实现
class AbstractFoo():
forceThis = 0
所以当我这样做时
class RealFoo(AbstractFoo):
pass
它抛出一个错误告诉我在我实现 forceThis
.
我该怎么做?
(我不希望该属性是只读的,但如果这是唯一的解决方案,我会接受。)
对于 class 方法,我发现我可以做到
from abc import ABCMeta, abstractmethod
class AbstractFoo(metaclass=ABCMeta):
@classmethod
@abstractmethod
def forceThis():
"""This must be implemented"""
所以
class RealFoo(AbstractFoo):
pass
至少会抛出错误 TypeError: Can't instantiate abstract class EZ with abstract methods forceThis
(虽然它不会强制 forceThis
成为 class 方法。)
如何让 class 属性弹出类似的错误消息?
您可以通过定义自己的元类来做到这一点。类似于:
class ForceMeta(type):
required = ['foo', 'bar']
def __new__(mcls, name, bases, namespace):
cls = super().__new__(mcls, name, bases, namespace)
for prop in mcls.required:
if not hasattr(cls, prop):
raise NotImplementedError('must define {}'.format(prop))
return cls
现在您可以将其用作您自己的元类类:
class RequiredClass(metaclass=ForceMeta):
foo = 1
这将引发错误 'must define bar'。
虽然您可以使用 metaclass 做一些非常相似的事情,如@Daniel Roseman 的
def check_reqs(cls):
requirements = 'must_have',
missing = [req for req in requirements if not hasattr(cls, req)]
if missing:
raise NotImplementedError(
'class {} did not define required attribute{} named {}'.format(
cls.__name__, 's' if len(missing) > 1 else '',
', '.join('"{}"'.format(name) for name in missing)))
return cls
@check_reqs
class Foo(object): # OK
must_have = 42
@check_reqs
class Bar(object): # raises a NotImplementedError
pass
我根据之前发布的内容想出了一个解决方案。 (谢谢@Daniel Roseman 和@martineau)
我创建了一个名为 ABCAMeta 的元class(最后一个 'A' 代表 'Attributes')。
class有两种工作方式。
A class 仅使用 ABCAMeta 作为元class 必须有一个名为
required_attributes
的 属性 应包含名称列表您想要在 class 的未来子 class 上需要的所有属性
父级元class为ABCAMeta的class必须具有其父级class(es)指定的所有必需属性。
例如:
class AbstractFoo(metaclass=ABCAMeta):
required_attributes = ['force_this']
class RealFoo(AbstractFoo):
pass
会抛出错误:
NameError: Class 'RealFoo' has not implemented the following attributes: 'force_this'
正是我想要的。
from abc import ABCMeta
class NoRequirements(RuntimeError):
def __init__(self, message):
RuntimeError.__init__(self, message)
class ABCAMeta(ABCMeta):
def __init__(mcls, name, bases, namespace):
ABCMeta.__init__(mcls, name, bases, namespace)
def __new__(mcls, name, bases, namespace):
def get_requirements(c):
"""c is a class that should have a 'required_attributes' attribute
this function will get that list of required attributes or
raise a NoRequirements error if it doesn't find one.
"""
if hasattr(c, 'required_attributes'):
return c.required_attributes
else:
raise NoRequirements(f"Class '{c.__name__}' has no 'required_attributes' property")
cls = super().__new__(mcls, name, bases, namespace)
# true if no parents of the class being created have ABCAMeta as their metaclass
basic_metaclass = True
# list of attributes the class being created must implement
# should stay empty if basic_metaclass stays True
reqs = []
for parent in bases:
parent_meta = type(parent)
if parent_meta==ABCAMeta:
# the class being created has a parent whose metaclass is ABCAMeta
# the class being created must contain the requirements of the parent class
basic_metaclass=False
try:
reqs.extend(get_requirements(parent))
except NoRequirements:
raise
# will force subclasses of the created class to define
# the attributes listed in the required_attributes attribute of the created class
if basic_metaclass:
get_requirements(cls) # just want it to raise an error if it doesn't have the attributes
else:
missingreqs = []
for req in reqs:
if not hasattr(cls, req):
missingreqs.append(req)
if len(missingreqs)!=0:
raise NameError(f"Class '{cls.__name__}' has not implemented the following attributes: {str(missingreqs)[1:-1]}")
return cls
欢迎在评论中提出任何改进建议。