假设我们有以下类层次结构:
class ClassA:
@property
def foo(self): return "hello"
class ClassB(ClassA):
@property
def bar(self): return "world"
如果我像这样在ClassB上探索 __ dict __ ,我只会看到bar属性:
for name,_ in ClassB.__dict__.items():
if name.startswith("__"):
continue
print(name)
输出为bar
我可以通过自己的方式来获取不仅指定类型而且还包含其祖先的属性。但是,我的问题是,在没有重新发明轮子的情况下,我是否已经在python中实现了这一目标。
def return_attributes_including_inherited(type):
results = []
return_attributes_including_inherited_helper(type,results)
return results
def return_attributes_including_inherited_helper(type,attributes):
for name,attribute_as_object in type.__dict__.items():
if name.startswith("__"):
continue
attributes.append(name)
for base_type in type.__bases__:
return_attributes_including_inherited_helper(base_type,attributes)
运行我的代码如下......
for attribute_name in return_attributes_including_inherited(ClassB):
print(attribute_name)
......给回了bar和foo。
请注意,我正在简化一些事情:名称冲突,使用items(),对于此示例,我可以使用dict,跳过以__开头的任何内容,忽略两个祖先本身具有共同祖先的可能性等。
EDIT1 - 我试图让这个例子变得简单。但我真的想要每个类和祖先类的属性名称和属性引用。下面的答案之一让我走得更好,当我开始工作时,我会发布一些更好的代码。
EDIT2 - 这就是我想要的,而且非常简洁。它基于以下Eli的答案。
def get_attributes(type):
attributes = set(type.__dict__.items())
for type in type.__mro__:
attributes.update(type.__dict__.items())
return attributes
它返回属性名称及其引用。
EDIT3 - 以下答案之一建议使用inspect.getmembers。这看起来非常有用,因为它就像dict一样,它只能在祖先类上运行。
由于我尝试做的很大一部分是找到标有特定描述符的属性,并包含祖先类,所以这里有一些代码可以帮助解决这个问题:
class MyCustomDescriptor:
# This is greatly oversimplified
def __init__(self,foo,bar):
self._foo = foo
self._bar = bar
pass
def __call__(self,decorated_function):
return self
def __get__(self,instance,type):
if not instance:
return self
return 10
class ClassA:
@property
def foo(self): return "hello"
@MyCustomDescriptor(foo="a",bar="b")
def bar(self): pass
@MyCustomDescriptor(foo="c",bar="d")
def baz(self): pass
class ClassB(ClassA):
@property
def something_we_dont_care_about(self): return "world"
@MyCustomDescriptor(foo="e",bar="f")
def blah(self): pass
# This will get attributes on the specified type (class) that are of matching_attribute_type. It just returns the attributes themselves, not their names.
def get_attributes_of_matching_type(type,matching_attribute_type):
return_value = []
for member in inspect.getmembers(type):
member_name = member[0]
member_instance = member[1]
if isinstance(member_instance,matching_attribute_type):
return_value.append(member_instance)
return return_value
# This will return a dictionary of name & instance of attributes on type that are of matching_attribute_type (useful when you're looking for attributes marked with a particular descriptor)
def get_attribute_name_and_instance_of_matching_type(type,matching_attribute_type):
return_value = {}
for member in inspect.getmembers(ClassB):
member_name = member[0]
member_instance = member[1]
if isinstance(member_instance,matching_attribute_type):
return_value[member_name] = member_instance
return return_value
答案 0 :(得分:9)
您应该使用python的inspect
模块来实现任何此类内省功能。
.
.
>>> class ClassC(ClassB):
... def baz(self):
... return "hiya"
...
>>> import inspect
>>> for attr in inspect.getmembers(ClassC):
... print attr
...
('__doc__', None)
('__module__', '__main__')
('bar', <property object at 0x10046bf70>)
('baz', <unbound method ClassC.baz>)
('foo', <property object at 0x10046bf18>)
详细了解inspect
模块here。
答案 1 :(得分:5)
您想使用dir
:
for attr in dir(ClassB):
print attr
答案 2 :(得分:2)
可悲的是,没有一个复合对象。 (普通)python对象的每个属性访问首先检查obj.__dict__
,然后检查它的所有基类的属性;虽然有一些内部缓存和优化,但是没有一个对象可以访问。
也就是说,可以改进代码的一件事是使用cls.__mro__
而不是cls.__bases__
...而不是类的直接父级,cls.__mro__
包含类的所有祖先,按照Python搜索的确切顺序,所有常见的祖先只发生一次。这也将允许您的类型搜索方法是非递归的。松散...
def get_attrs(obj):
attrs = set(obj.__dict__)
for cls in obj.__class__.__mro__:
attrs.update(cls.__dict__)
return sorted(attrs)
...对默认dir(obj)
实现进行了合理的近似。
答案 3 :(得分:1)
这是我在当天写的一个函数。最好的答案是使用inspect
模块,因为使用__dict__
为我们提供了所有函数(我们的+继承的)和(ALL?)数据成员和属性。 inspect
为我们提供了足够的信息来清除我们不想要的东西。
def _inspect(a, skipFunctionsAlways=True, skipMagic = True):
"""inspects object attributes, removing all the standard methods from 'object',
and (optionally) __magic__ cruft.
By default this routine skips __magic__ functions, but if you want these on
pass False in as the skipMagic parameter.
By default this routine skips functions, but if you want to see all the functions,
pass in False to the skipFunctionsAlways function. This works together with the
skipMagic parameter: if the latter is True, you won't see __magic__ methods.
If skipFunctionsAlways = False and skipMagic = False, you'll see all the __magic__
methods declared for the object - including __magic__ functions declared by Object
NOT meant to be a comprehensive list of every object attribute - instead, a
list of every object attribute WE (not Python) defined. For a complete list
of everything call inspect.getmembers directly"""
objType = type(object)
def weWantIt(obj):
#return type(a) != objType
output= True
if (skipFunctionsAlways):
output = not ( inspect.isbuiltin(obj) ) #not a built in
asStr = ""
if isinstance(obj, types.MethodType):
if skipFunctionsAlways: #never mind, we don't want it, get out.
return False
else:
asStr = obj.__name__
#get just the name of the function, we don't want the whole name, because we don't want to take something like:
#bound method LotsOfThings.bob of <__main__.LotsOfThings object at 0x103dc70>
#to be a special method because it's module name is special
#WD-rpw 02-23-2008
#TODO: it would be great to be able to separate out superclass methods
#maybe by getting the class out of the method then seeing if that attribute is in that class?
else:
asStr = str(obj)
if (skipMagic):
output = (asStr.find("__") == -1 ) #not a __something__
return (output)
for value in inspect.getmembers( a, weWantIt ):
yield value
答案 4 :(得分:0)
{k: getattr(ClassB, k) for k in dir(ClassB)}
使用<property object...>
实例时,将显示正确的值(而不是ClassB
)。
当然,您可以通过添加if not k.startswith('__')
之类的内容来过滤此内容。