2017-07-26 107 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

相关问题