耶!元类的实际应用不是为了使用元类而设计的!我们可以编写一个元类,如果在类定义中出现意外的属性,它将抛出。我们所需要做的就是确保你的程序员真正使用它。
class RequiredFieldsMeta(type):
_interface = {'company_name', 'num_employees'}
def __new__(cls, clsname, bases, attrs):
for field in RequiredFieldsMeta._interface:
if field not in attrs:
raise AttributeError(
'Class %s missing required property %s'
% (clsname, field))
for name in attrs:
if not isdunder(name) and name not in RequiredFieldsMeta._interface:
raise AttributeError(
'Class %s has extra property %s'
% (clsname, name))
return super(RequiredFieldsMeta, cls).__new__(cls, clsname, bases, attrs)
# Works fine:
class MongoCompany(metaclass=RequiredFieldsMeta):
company_name = 'Mongo Inc.'
num_employees = 100
# Throws AttributeError:
class ESyCompany(metaclass=RequiredFieldsMeta):
extra_prop = 'foobar'
Here's一个快速演示
注意如何,我们甚至不使其向实例:类本身的定义时,我们的支票总是会自动运行。
编辑:在我的编辑中,我引用了一个函数is_dunder
。这可以像name.startswith('__')
或者正则表达式或者任何你想要的那样简单,只要它去掉python而不是程序员放在类中的属性。
编辑2:只是为了好玩,这里有两个,更 “优雅” 我们检查的(尽管不那么具体的)实现:
def __new__(cls, clsname, bases, attrs):
attr_names = {a for a in attrs if not is_dunder(a)}
if attr_names.difference(RequiredFieldsMeta._interface):
raise AttributeError('Class %s has extra properties' % clsname)
if RequiredFieldsMeta._interface.difference(attr_names):
raise AttributeError('Class %s missing required properties' % clsname)
return super(RequiredFieldsMeta, cls).__new__(cls, clsname, bases, attrs)
或者干脆:
def __new__(cls, clsname, bases, attrs):
attr_names = {a for a in attrs if not is_dunder(a)}
if attr_names != RequiredFieldsMeta._interface:
raise AttributeError(
'Class %s does not match the required interface' % clsname)
return super(RequiredFieldsMeta, cls).__new__(cls, clsname, bases, attrs)
在第一脸红,你是否熟悉[inheritance]的概念(http://www.python-course.eu/python3_inheritance.php)? – Skam
您可以创建一个“接口”类,该接口类既可以通过'throw NotImplementedError'来继承所需的各种方法。 –
@MateenUlhaq它不会阻止程序员向“子类”添加比“接口”类更多的属性。 – kakarukeys