2010-11-22 6 views
22

クラスを検査する方法が必要なので、ユーザー定義のクラス属性である属性を安全に識別できます。問題は、dir()、inspect.getmembers()、friendsなどの関数は、__class__,__doc__,__dict____hash__などの定義済みのクラス属性をすべて返します。これは当然理解できますが、名前を付けるメンバのリストを無視するだけでよいと主張することもできますが、残念ながらこれらの事前定義された属性は異なるバージョンのPythonで変更されるため、私のプロジェクトはPythonプロジェクト私はそれが好きではありません。Pythonクラス属性を検査する

例:それはインスタンス属性であるとして、私は唯一のユーザー定義クラスの属性を取得する安全な方法[「A」、「B」]ない「C」をしたい上記の例で

>>> class A: 
... a=10 
... b=20 
... def __init__(self): 
...  self.c=30 
>>> dir(A) 
['__doc__', '__init__', '__module__', 'a', 'b'] 
>>> get_user_attributes(A) 
['a','b'] 

。だから私の質問は...誰も上記の架空の関数で私を助けることができるget_user_attributes(cls)

P.S.私はASTレベルのクラスを解析することで問題を解決しようとしていましたが、これは非常に簡単です。しかし、私はすでに解析されたオブジェクトをASTノードツリーに変換する方法を見つけることができません。私は、クラスがバイトコードにコンパイルされると、すべてのAST情報は破棄されると思います。 「特別な属性」の両端にヤコブ

+1

あなたはあなたがASTでそれをしようとしていると言います。つまり、スーパークラスではなくクラスですぐに定義された属性だけが必要なのでしょうか?私はあなたが「組み込みのもの」を望んでいないことを認識していますが、私はこの問題を混乱させています。 – aaronasterling

答えて

27

が難しい方法です。ここに簡単な方法があります。なぜそれが私に早く起こらなかったのか分からない。

import inspect 

def get_user_attributes(cls): 
    boring = dir(type('dummy', (object,), {})) 
    return [item 
      for item in inspect.getmembers(cls) 
      if item[0] not in boring] 

ここでこれはかなり強固でなければなりません開始

def get_user_attributes(cls): 
    boring = dir(type('dummy', (object,), {})) 
    attrs = {} 
    bases = reversed(inspect.getmro(cls)) 
    for base in bases: 
     if hasattr(base, '__dict__'): 
      attrs.update(base.__dict__) 
     elif hasattr(base, '__slots__'): 
      if hasattr(base, base.__slots__[0]): 
       # We're dealing with a non-string sequence or one char string 
       for item in base.__slots__: 
        attrs[item] = getattr(base, item) 
      else: 
       # We're dealing with a single identifier as a string 
       attrs[base.__slots__] = getattr(base, base.__slots__) 
    for key in boring: 
     del attrs['key'] # we can be sure it will be present so no need to guard this 
    return attrs 

です。基本的には、デフォルトのサブクラスである属性objectを無視することによって動作します。次に、渡されたクラスのmroを取得し、それを逆順に走査して、サブクラスのキーがスーパークラスのキーを上書きできるようにします。これは、キーと値のペアの辞書を返します。あなたは、キーのリストをしたい場合は、inspect.getmembersのように値のタプルは、ただのPython 3にattrs.items()またはlist(attrs.items())のいずれかを返す

あなたが実際にMROを横断したいとちょうどサブクラスで直接定義された属性を使用しない場合、それはですより簡単:

+3

'__slots__'? ;-) –

+1

@Chris Morgan。良い目。他に何か間違っている? – aaronasterling

+0

そうは思わない。私はちょうど当時の '__slots__ 'について考えていたので、悪魔の主張をしていた。(PyPyで何かを見つけようとした) –

6

ダブルアンダー

敬具は2.0前のpythonの一部となっています。彼らが近い将来いつでもそれを変えることはほとんどあり得ないだろう。

class Foo(object): 
    a = 1 
    b = 2 

def get_attrs(klass): 
    return [k for k in klass.__dict__.keys() 
      if not k.startswith('__') 
      and not k.endswith('__')] 

print get_attrs(Foo) 

[ 'A'、 'B']以下

+2

ユーザ定義の '__add__'、' __mul__'、 '__iter__'などはどうですか? – aaronasterling

+0

もしそれらがユーザー定義のものであれば、それらも必要です。すでに解析され、バイトコンパイルされているクラスのASTツリーを取得することは可能ですか? –

+0

@jakob、そうではありませんが、ソースコードは解析されずにメモリに保持されず、ソースコードがなくてもPythonのバイトコードが不足するため、ASTは存在しない可能性があります。 – toriningen

2

新しいスタイルクラスを使用する場合、親クラスの属性を単純に引き算できますか?

class A(object): 
    a = 10 
    b = 20 
    #... 

def get_attrs(Foo): 
    return [k for k in dir(Foo) if k not in dir(super(Foo))] 

編集:かなりありません。 __dict__,__module__および__weakref__は、オブジェクトから継承したときに表示されますが、オブジェクト自体には存在しません。あなたは特別な場合があります - 私は彼らが非常に頻繁に変化するだろうとは思わない。 aaronasterling

3

おかげで、あなたは私に私が私の最後のクラス属性インスペクタ機能 :-)必要な表現を与え、次のようになります

def get_user_attributes(cls,exclude_methods=True): 
    base_attrs = dir(type('dummy', (object,), {})) 
    this_cls_attrs = dir(cls) 
    res = [] 
    for attr in this_cls_attrs: 
    if base_attrs.count(attr) or (callable(getattr(cls,attr)) and exclude_methods): 
     continue 
    res += [attr] 
    return res 

どちらのリターンクラス属性は(exclude_methods = true)をvariabelsかも取得メソッド。 上記の関数の初期テストでは、古いスタイルと新しいスタイルの両方のPythonクラスがサポートされています。

/jakob

+0

nice。 1つの改善点は、 'exclude_methods'と' callable(getattr(...)) 'のチェックを切り替えて、単純なブールチェックに失敗した場合にのみ 'callable'が実行されるようにすることです。 – aaronasterling

+0

あなたはとても正しいです:-) –

関連する問題