2017-07-26 6 views
4

私はPythonでクラスインタフェースを構築しようとしていますが、Pythonにはインタフェース構造がありません。私は(コンパイル時や実行時)、プログラマが別のクラスに同じ名前のプロパティを追加することなく、一つのクラスで新しいプロパティを追加しようとした場合、例外を発生させることで必要なもの2つのPythonクラスが同じ名前のプロパティを持っていることを確認してください

例:プログラマがESCompanyを変更せずにMongoCompanyにフィールドを追加しようとした場合

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() 

編集

背景 これはelasticsearch-DSLのDocTypeを持つ別のファイルで宣言Elasticsearchのスキーマに対応する変更を加えることなくMongoengineのDocumentクラスで宣言されたのMongoDBのスキーマを変更することからプログラマを防ぐためのものですクラス。

+0

最初にあなたは[継承](http://www.python-course.eu/python3_inheritance.php)の概念に精通していますか? – Skam

+1

必要なさまざまなメソッドで 'throw NotImplementedError'sから継承する" interface "クラスを作成できます。 –

+0

@MateenUlhaqプログラマが "interface"クラスよりも多くの属性をサブクラスに追加するのを妨げることはありません。 – kakarukeys

答えて

6

yay!メタクラスの使用のためだけには工夫されていないメタクラスの実際のアプリケーション!予期せぬプロパティがクラス定義に現れた場合に投げられるメタクラスを書くことができます。私たちがする必要があるのは、プログラマーが実際にそれを使用することだけです。クラス自体が定義されているときに私たちのチェックが実行されます:私たちも、インスタンス化することをしない方法を

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がクラスに入れたプロパティを取り除いている限りです。


EDIT 2:ちょうど楽しみのために、ここで私たちのチェックの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だと信じています。あなたのクイックデモを実行しています。それはtypo num_employessなので、他に何かがあります..... – kakarukeys

関連する問題