有没有办法访问包含基类的 __dict__(或类似的东西)?
假设我们有以下的类结构:
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两个属性。
需要注意的是,我在简化一些内容:比如属性名冲突、在这个例子中我可以直接用dict而不是items()、跳过以__开头的属性、忽略两个父类可能有共同的祖先等等。
编辑1 - 我试着保持例子简单。但我其实想要每个类和父类的属性名和属性引用。下面的一个回答让我找到了更好的方向,等我把代码弄好再分享。
编辑2 - 这个方法实现了我想要的,而且非常简洁。它基于下面Eli的回答。
def get_attributes(type):
attributes = set(type.__dict__.items())
for type in type.__mro__:
attributes.update(type.__dict__.items())
return attributes
它返回了属性名和它们的引用。
编辑3 - 下面的一个回答建议使用inspect.getmembers。这个方法看起来非常有用,因为它不仅适用于当前类,还能操作父类。
由于我想要找到带有特定描述符的属性,并且包括父类,这里有一些代码可以帮助实现这个目标,希望对其他人也有帮助:
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
5 个回答
很遗憾,Python中并没有一个单一的复合对象。每次访问一个普通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)
功能。
你想要使用 dir
这个功能:
for attr in dir(ClassB):
print attr
你应该使用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
模块的信息,可以点击这里。