2016-08-25 3 views
0

host_name、kernel_version、bios_versionなどのコンピュータに関する詳細を取得するためのクラスを作成しています。いくつかの詳細は他のものよりも高価ですので、私はそれらを取得するget_ *関数を持っていますが、それらが再び必要になった場合にはオブジェクトにキャッシュされた結果を保持します。私は、カーネルのバージョンはように取得することができるので、辞書オブジェクトのように見えるようにそれらを実装検討しています:固定の読み取り専用フィールドの動的属性またはカスタム辞書

system = System() 
kver = system['kernel_version'] 

これは、データを取得するために内部的にインスタンスメソッドget_kernel_version(self)を呼び出します。上記のインスタンス化されたオブジェクトから2回目にカーネルのバージョンが取得された場合、元の呼び出しのキャッシュ結果がget_kernel_version(self)に返されます。これらのキーと値のペアはすべて読み取り専用であり、利用可能なget_ *メソッドに基づいて固定数があり、後で追加することはできないため、通常の辞書のようには感じられません。 values()のような関数を呼び出す必要はありません。この関数は、すべてのget_ *関数が不必要にヒットすることになります。また、構文は私が望むよりも少し冗長です。代わりにsystem.kernel_versionを使用すると、この使用例の方が自然なようです。

クラスインスタンスで動的属性を使用する方がよいかどうか検討しています。しかし、すべての属性のリストを取得するための自然な方法が必要ですが、それらをサポートする内部メソッドは必要ありません。おそらく__dir__特別な方法を使用して、keys()の辞書リストに似たリストを返します。カーネルバージョンとホスト名がリストに表示されますが、__class__またはget_kernel_versionは表示されません。これは、__dir__の定義のための推奨された習慣に反するように思われるので、これが正しいアプローチであるかどうかはわかりません。

適切な属性が既に定義されていない場合、get_ *関数を使用して具体的なクラスにコールバックするプロキシクラスインスタンスを返すことができます。ここで

私は辞書のアプローチを実装して実験していたバージョンの例です:

次の出力を生成
class System(dict): 
    def __getitem__(self, key): 
     try: 
      return getattr(self, 'get_'+key)() 
     except AttributeError as ex: 
      raise KeyError(ex.message) 
    def __setitem__(self, key, value): 
     raise Exception('Read-only') 
    def __delitem__(self, key, value): 
     raise Exception('Read-only') 
    def keys(self): 
     return [ x[4:] for x in dir(self) if x.startswith('get_') ] 
    def get_host_name(self): 
     return 'localhost' 
    def get_kernel_version(self): 
     return '4.7.0' 

system = System() 
print repr(system.keys()) 
for key in system.keys(): 
    print '{0}: {1}'.format(key, system[key]) 

try: 
    system['bios'] 
except Exception as ex: 
    print str(ex) 

try: 
    system['kernel_version'] = '5.0' 
except Exception as ex: 
    print str(ex) 

['host_name', 'kernel_version'] 
host_name: localhost 
kernel_version: 4.7.0 
"'System' object has no attribute 'get_bios'" 
Read-only 

上記のコードはまだキャッシュを実装していません。値はまだありませんが、追加するのは簡単です。しかし、私は属性としてこれを行うべきであるように感じています。私はそれを行うときに私がkeys()と同じ機能をエミュレートするために__dir__を乱用すべきかどうかだけはわかりません。

私は、読み取り専用辞書をエミュレートするか、動的属性を持つクラスインスタンスを提示する必要がありますか?

+0

あなたができるようになる。このような何か[ 'AttrDict'レシピ](https://code.activestate.com/recipes/576972-attrdict/)を使用することができます(__getattr __()を使って内容へのアクセスを制御する方法もあります)どちらの方法でもキャッシュをサポートすることができます[あなたの質問を編集し、特にいくつかのサンプルコードを追加することでより具体的にすると、おそらく追加の詳細を提供することができます。 – martineau

+0

@martineau私がlookiって何の基本的な考え方の例を追加しました〜のために。 – penguin359

+0

あなたのコードを見て、もう少し考えてみたら、あなたが持っているものは基本的にOKだと思います。あなたが持っている 'keys()'メソッドのデータを自動的に生成するように拡張することができます。下記の[私の答え](http://stackoverflow.com/a/39171820/355230)を参照してください。 – martineau

答えて

0

私はあなたが使用している読み取り専用ディクショナリのサブクラスのアプローチを固執するのがいいと思います。しかし、特定のサブクラスを派生させ、keys()メソッドによって返された値を作成するためにメタクラスを使用する汎用の読み取り専用ディクショナリスーパークラスを作成することで、実装をいくらか改善できます。両方を行うことは下記の通りです。

これはもうdir()(「__dir__」という属性はありません)を「悪用する」必要がないことを意味します。一般的なMetaReadonlyDictおよびReadonlyDictクラスを再利用して、他の同様のタイプを作成することもできます。

class MetaReadonlyDict(type): 
    def __new__(mcls, classname, bases, classdict): 
     classobj = type.__new__(mcls, classname, bases, classdict) 
     prefix = classdict['prefix'] 
     _keys = set(name[len(prefix):] for name in classdict 
             if name.startswith(prefix)) 
     setattr(classobj, 'keys', lambda self: list(_keys)) # define keys() 
     return classobj 

class ReadonlyDict(dict): 
    def __getitem__(self, key): 
     try: 
      return getattr(self, self.prefix + key)() 
     except AttributeError as ex: 
      raise Exception(
       "{} object has no {!r} key".format(self.__class__.__name__, key)) 
    def __setitem__(self, key, value): 
     verb = "redefined" if key in self else "defined" 
     raise Exception(
      "{} object is read-only: {!r} " 
      "key can not be {}".format(self.__class__.__name__, key, verb)) 
    def __delitem__(self, key): 
     raise Exception(
      "{} object is read-only: {!r} " 
      "key can not be deleted".format(self.__class__.__name__, key)) 
    def __contains__(self, key): 
     return key in self.keys() 

class System(ReadonlyDict): 
    __metaclass__ = MetaReadonlyDict 
    prefix = '_get_' 

    def _get_host_name(self): 
     return 'localhost' 
    def _get_kernel_version(self): 
     return '4.7.0' 

system = System() 
print('system.keys(): {!r}'.format(system.keys())) 
print('values associated with system.keys():') 
for key in system.keys(): 
    print(' {!r}: {!r}'.format(key, system[key])) 

try: 
    system['bios'] 
except Exception as ex: 
    print(str(ex)) 

try: 
    system['kernel_version'] = '5.0' 
except Exception as ex: 
    print(str(ex)) 

try: 
    del system['host_name'] 
except Exception as ex: 
    print(str(ex)) 

出力:

system.keys(): ['kernel_version', 'host_name'] 
values associated with system.keys(): 
    'kernel_version': '4.7.0' 
    'host_name': 'localhost' 
System object has no 'bios' key 
System object is read-only: 'kernel_version' key can not be redefined 
System object is read-only: 'host_name' key can not be deleted 
関連する問題