38

どの属性がユーザー定義のクラス属性であるかを安全に識別できるように、クラスを検査する方法が必要です。問題は、dir() 、inspect.getmembers()などの関数が、、、、、などの事前定義された属性を含むすべてのクラス属性を返すこと__class__です。これはもちろん理解でき、無視する名前付きメンバーのリストを作成することもできますが、残念ながら、これらの事前定義された属性はPythonのさまざまなバージョンで変更されるため、Pythonプロジェクトでプロジェクトを変更できるようになります。 -そして私はそれが好きではありません。__doc____dict____hash__

例:

>>> 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']

上記の例では、インスタンス属性であるため、「c」ではなく、ユーザー定義のクラス属性['a'、'b']のみを取得する安全な方法が必要です。だから私の質問は...誰かが上記の架空の機能で私を助けることができますget_user_attributes(cls)か?

私は、非常に簡単なASTレベルでクラスを解析することによって問題を解決しようとしばらく時間を費やしました。しかし、すでに解析されたオブジェクトをASTノードツリーに変換する方法が見つかりません。クラスがバイトコードにコンパイルされると、すべてのAST情報が破棄されると思います。

4

6 に答える 6

36

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

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を取得し、逆の順序でトラバースして、サブクラスキーがスーパークラスキーを上書きできるようにします。キーと値のペアの辞書を返します。キーのリストが必要な場合は、inのような値タプルをinspect.getmembers返すか、attrs.items()Python3で返しますlist(attrs.items())

実際にmroをトラバースするのではなく、サブクラスで直接属性を定義するだけの場合は、次のようになります。

def get_user_attributes(cls):
    boring = dir(type('dummy', (object,), {}))
    if hasattr(cls, '__dict__'):
        attrs = cls.__dict__.copy()
    elif hasattr(cls, '__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
于 2010-11-22T00:13:05.600 に答える
8

「特別な属性」の両端にある二重アンダースコアは、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']

于 2010-11-22T00:09:29.323 に答える
4

aaronasterling に感謝します。必要な式を教えてくれました :-) 最終的なクラス属性インスペクタ関数は次のようになります。

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) か、メソッドも取得します。上記の関数に対する私の最初のテストでは、古いスタイルと新しいスタイルの両方の Python クラスがサポートされています。

/ヤコブ

于 2010-11-22T07:46:02.897 に答える
3

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

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__オブジェクトから継承するときに表示されますが、オブジェクト自体には存在しません。あなたはこれらを特別な場合にすることができます-私はそれらが非常に頻繁に変わるとは思えません。

于 2010-11-22T00:15:05.653 に答える