Inspect python class attributes
我需要一种检查类的方法,以便安全地识别哪些属性是用户定义的类属性。问题是dir()、inspect.getmembers()和friends等函数返回所有类属性,包括预定义的属性,如:
例子:
1 2 3 4 5 6 7 8 9 | >>> 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'] |
在上面的示例中,我希望有一种安全的方法,只检索用户定义的类属性['a'、'b']而不是'c',因为它是一个实例属性。所以我的问题是…有人能帮我完成上述虚构的功能吗?
另外,我花了一些时间试图通过解析AST级别的类来解决这个问题,这非常容易。但我找不到将已经解析的对象转换为AST节点树的方法。我猜一旦一个类被编译成字节码,所有的AST信息都会被丢弃。
祝雅各布好运
下面是一条艰难的路。这是简单的方法。不知道为什么我没早点想到。
1 2 3 4 5 6 7 | 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] |
号
这是个开始
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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 |
这应该是相当强大的。本质上,它的工作方式是让
如果您实际上不想遍历MRO,只想直接在子类上定义属性,那么更容易:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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 |
。
"特殊属性"两端的双下划线是2.0之前Python的一部分。他们不太可能在不久的将来随时改变这一点。
1 2 3 4 5 6 7 8 9 10 | 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']
号
谢谢你,你给了我一个我需要的表情。我的最后一个类属性检查器函数如下所示:
1
2
3
4
5
6
7
8
9 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。
或者只返回类属性variabels(exclude_methods=true),或者也检索这些方法。我最初测试的是上面的函数,它既支持旧的也支持新的python类。
/雅各布
如果使用新的样式类,可以简单地减去父类的属性吗?
1
2
3
4
5
6
7 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__ ,但客体本身并不存在。你可以对这些进行特殊处理——我怀疑它们会经常改变。抱歉,内格罗撞到了线。我很惊讶,到2019年还没有简单的函数(或库)来处理这种常见的用法。
我要感谢阿隆斯特林的想法。实际上,
set 容器提供了一种更直接的表达方式:
1
2
3
4
5
6
7 class dummy: pass
def abridged_set_of_user_attributes(obj):
return set(dir(obj))-set(dir(dummy))
def abridged_list_of_user_attributes(obj):
return list(abridged_set_of_user_attributes(obj))。
使用列表理解的原始解决方案实际上是两个级别的循环,因为有两个
in 关键字组合在一起,尽管只有一个for 关键字使它看起来不像是工作。