耶!元類的實際應用不是爲了使用元類而設計的!我們可以編寫一個元類,如果在類定義中出現意外的屬性,它將拋出。我們所需要做的就是確保你的程序員真正使用它。
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