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