声明抽象类属性的最具python风格的方法
假设您正在编写一个抽象类,其一个或多个非抽象类方法要求具体类具有特定的类属性;e、 例如,如果可以通过匹配不同的正则表达式来构造每个具体类的实例,那么您可能希望为ABC提供以下信息:声明抽象类属性的最具python风格的方法,python,python-3.x,abstract-class,Python,Python 3.x,Abstract Class,假设您正在编写一个抽象类,其一个或多个非抽象类方法要求具体类具有特定的类属性;e、 例如,如果可以通过匹配不同的正则表达式来构造每个具体类的实例,那么您可能希望为ABC提供以下信息: @classmethod def parse(cls, s): m = re.fullmatch(cls.PATTERN, s) if not m: raise ValueError(s) return cls(**m.groupdict()) (也许这可以通过自定义元类更
@classmethod
def parse(cls, s):
m = re.fullmatch(cls.PATTERN, s)
if not m:
raise ValueError(s)
return cls(**m.groupdict())
(也许这可以通过自定义元类更好地实现,但为了示例起见,请尝试忽略这一点。)
现在,因为抽象方法和属性的重写是在实例创建时检查的,而不是在子类创建时检查的,所以尝试使用abc.abstractmethod
来确保具体类具有模式
属性将不起作用-但肯定应该有东西告诉任何查看代码的人“我没有忘记在ABC上定义模式
;具体的类应该定义它们自己的类。”问题是:哪种东西是最具python风格的
@property
@abc.abstractmethod
def PATTERN(self):
pass
@property
@classmethod
@abc.abstractmethod
def PATTERN(cls):
pass
(顺便说一句,假设Python3.4或更高版本。)这可能会让读者产生误解,因为这意味着PATTERN
应该是实例属性而不是类属性@property
@abc.abstractmethod
def PATTERN(self):
pass
@property
@classmethod
@abc.abstractmethod
def PATTERN(cls):
pass
这可能会让读者感到非常困惑,因为@property
和@classmethod
通常不能组合;它们只在这里一起工作(对于给定的“work”值),因为方法一旦被重写就会被忽略PATTERN = ''
PATTERN = None
如果一个具体类无法定义自己的模式
,解析
将只接受空输入。此选项并不广泛适用,因为并非所有用例都有适当的伪值PATTERN = ''
PATTERN = None
如果一个具体的类未能定义自己的模式
,解析
将引发一个错误,程序员将得到他们应得的模式
属性Python>=3.6版本
(向下滚动寻找一个适用于Python的版本我已经搜索了很长一段时间了,直到昨天我才决定深入研究它。我喜欢很多,但是缺少两件事:允许一个抽象类有一个子类本身是抽象的,并且很好地使用类型提示和静态类型工具,例如(这是有道理的,因为早在2017年,这几乎不是一件事) 我开始着手用我自己的解决方案在这里写一个回复,但是我意识到我需要大量的测试和文档,所以我把它做成了一个合适的python模块: 使用(自版本0.9.5起): 类解析器(acp.Abstract): 模式:str=acp.abstract\u class\u属性(str) @类方法 def解析(cls,s): m=重新完全匹配(cls模式,s) 如果不是m: 提升值错误 返回cls(**m.groupdict()) 类FooBarParser(解析器): 模式=r“foo\s+bar” 定义初始化(…):。。。 类SpamParser(解析器): 图案=r“(垃圾邮件)+鸡蛋” 定义初始化(…):。。。
请参阅以了解or的充分使用。Python是否主观?我已经了解到元类有助于增强使用它的对象的外观。@wwii是的,它们也可以用于此目的,但在本例中,这不仅仅是外观。与抽象基类一样,元类也可以增强特定类的属性但是E是在定义时定义的,不仅仅是外观或感觉…这正是我所说的外观和感觉-我应该更精确。THNX因为
NotImplemented
是一个单例,通常与is
class RequirePatternMeta(type):
"""Metaclass that enforces child classes define PATTERN."""
def __init__(cls, name, bases, attrs):
# Skip the check if there are no parent classes,
# which allows base classes to not define PATTERN.
if not bases:
return
if attrs.get('PATTERN', NotImplemented) is NotImplemented:
# Choose your favorite exception.
raise NotImplementedError('You forgot to define PATTERN!!!')
class PatternDefiningBase(metaclass=RequirePatternMeta):
# Dear programmer: implement this in a subclass OR YOU'LL BE SORRY!
PATTERN = NotImplemented
@classmethod
def sample(cls):
print(cls.PATTERN)
class LegalPatternChild(PatternDefiningBase):
PATTERN = r'foo\s+bar'
class IllegalPatternChild(PatternDefiningBase):
pass
from abs import ABCMeta, abstractmethod
ABCRequirePatternMeta = type('ABCRequirePatternMeta', (ABCMeta, RequirePatternMeta), {})
class PatternDefiningBase(metaclass=ABCRequirePatternMeta):
# Dear programmer: implement this in a subclass OR YOU'LL BE SORRY!
PATTERN = NotImplemented
@classmethod
def sample(cls):
print(cls.PATTERN)
@abstractmethod
def abstract(self):
return 6
class LegalPatternChild(PatternDefiningBase):
PATTERN = r'foo\s+bar'
def abstract(self):
return 5
class IllegalPatternChild1(PatternDefiningBase):
PATTERN = r'foo\s+bar'
print(LegalPatternChild().abstract())
print(IllegalPatternChild1().abstract())
class IllegalPatternChild2(PatternDefiningBase):
pass
5
TypeError: Can't instantiate abstract class IllegalPatternChild1 with abstract methods abstract
# Then the NotImplementedError if it kept on going.