在描述我的解决方案之前,先给大家介绍一下Python类实例是如何创建的:
图1:Python实例创建[1]
根据上面的描述,你可以看到在 Python 中类的实例实际上是由一个 Metaclass 创建的。正如我们所见,当调用者创建我们类的实例时,首先调用__call__ 魔术方法,该方法依次调用类的__new__ 和__init__,然后__cal__ 返回对象实例返回给调用者。
话虽如此,我们可以简单地尝试检查__init__ 创建的实例是否确实定义了那些“必需”属性。
元类
class ForceRequiredAttributeDefinitionMeta(type):
def __call__(cls, *args, **kwargs):
class_object = type.__call__(cls, *args, **kwargs)
class_object.check_required_attributes()
return class_object
正如您在__call__ 中看到的,我们所做的是创建类对象,然后调用其check_required_attributes() 方法,该方法将检查是否已定义所需的属性。如果没有定义所需的属性,我们应该简单地抛出一个错误。
超类
Python 2
class ForceRequiredAttributeDefinition(object):
__metaclass__ = ForceRequiredAttributeDefinitionMeta
starting_day_of_week = None
def check_required_attributes(self):
if self.starting_day_of_week is None:
raise NotImplementedError('Subclass must define self.starting_day_of_week attribute. \n This attribute should define the first day of the week.')
Python 3
class ForceRequiredAttributeDefinition(metaclass=ForceRequiredAttributeDefinitionMeta):
starting_day_of_week = None
def check_required_attributes(self):
if self.starting_day_of_week is None:
raise NotImplementedError('Subclass must define self.starting_day_of_week attribute. \n This attribute should define the first day of the week.')
这里我们定义了实际的超类。三件事:
- 应该使用我们的元类。
- 应将所需属性定义为
None,参见starting_day_of_week = None
- 应实现
check_required_attributes 方法,检查所需属性是否为None,以及是否要向用户抛出带有合理错误消息的NotImplementedError。
工作和非工作子类的示例
class ConcereteValidExample(ForceRequiredAttributeDefinition):
def __init__(self):
self.starting_day_of_week = "Monday"
class ConcereteInvalidExample(ForceRequiredAttributeDefinition):
def __init__(self):
# This will throw an error because self.starting_day_of_week is not defined.
pass
输出
Traceback (most recent call last):
File "test.py", line 50, in <module>
ConcereteInvalidExample() # This will throw an NotImplementedError straightaway
File "test.py", line 18, in __call__
obj.check_required_attributes()
File "test.py", line 36, in check_required_attributes
raise NotImplementedError('Subclass must define self.starting_day_of_week attribute. \n This attribute should define the first day of the week.')
NotImplementedError: Subclass must define self.starting_day_of_week attribute.
This attribute should define the first day of the week.
如您所见,成功创建的第一个实例定义了所需的属性,第二个实例直接引发了NotImplementedError。