2017-07-26 168 views
4

我期待在Python中構建類接口,但發現Python缺少接口結構。確保兩個Python類具有相同名稱的屬性

我需要的是,如果一個程序員試圖在一個類中增加新的屬性,而無需在另一個類具有相同的名稱添加屬性,一個異常要提高(在編譯時或運行時)

一個例子:如果程序員嘗試將字段添加到MongoCompany不改變ESCompany

class MongoCompany: 
    company_name = MongoField() 

class ESCompany: 
    company_name = ESField() 

一個將引發異常。

class MongoCompany: 
    company_name = MongoField() 
    company_phone = MongoField() 

class ESCompany: 
    company_name = ESField() 

MongoCompany.init() 

編輯

背景 這是爲了阻止程序員修改與Mongoengine的Document類中聲明MongoDB的模式,而不添加相應的修改,在另一個文件與elasticsearch-DSL的DocType宣佈Elasticsearch的架構類。

+0

在第一臉紅,你是否熟悉[inheritance]的概念(http://www.python-course.eu/python3_inheritance.php)? – Skam

+1

您可以創建一個「接口」類,該接口類既可以通過'throw NotImplementedError'來繼承所需的各種方法。 –

+0

@MateenUlhaq它不會阻止程序員向「子類」添加比「接口」類更多的屬性。 – kakarukeys

回答

6

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

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) 
+0

啊謝謝,我認爲這正是我所需要的,但代碼無法按預期工作? MongoCompany拋出一個錯誤。但我明白了。 – kakarukeys

+0

使用元類的語法在python 2和3之間更改。您使用哪個版本? – Will

+0

我相信它是3.6.1,我正在運行你的快速演示。這是一個錯誤num_employess,還有別的..... – kakarukeys

相關問題