有没有办法访问包含基类的 __dict__ (或类似的东西)?

2022-01-13 00:00:00 python attributes

问题描述

假设我们有以下类层次结构:

Suppose we have the following class hierarchy:

class ClassA:

    @property
    def foo(self): return "hello"

class ClassB(ClassA):

    @property
    def bar(self): return "world"

如果我像这样在 ClassB 上探索 __dict__,我只会看到 bar 属性:

If I explore __dict__ on ClassB like so, I only see the bar attribute:

for name,_ in ClassB.__dict__.items():

    if name.startswith("__"):
        continue

    print(name)

输出是条形

我可以使用自己的方法来获取指定类型及其祖先的属性.但是,我的问题是,python 中是否已经有一种方法可以让我在不重新发明轮子的情况下做到这一点.

I can roll my own means to get attributes on not only the specified type but its ancestors. However, my question is whether there's already a way in python for me to do this without re-inventing a wheel.

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)

如下运行我的代码...

Running my code as follows...

for attribute_name in return_attributes_including_inherited(ClassB):
    print(attribute_name)

...返回 bar 和 foo.

... gives back both bar and foo.

请注意,我正在简化一些事情:名称冲突、在本示例中我可以使用 dict 时使用 items()、跳过以 __ 开头的任何内容、忽略两个祖先本身具有共同祖先的可能性等.

Note that I'm simplifying some things: name collisions, using items() when for this example I could use dict, skipping over anything that starts with __, ignoring the possibility that two ancestors themselves have a common ancestor, etc.

EDIT1 - 我试图使示例保持简单.但我真的想要每个类和祖先类的属性名称和属性引用.下面的答案之一让我走上了更好的轨道,当我让它工作时,我会发布一些更好的代码.

EDIT1 - I tried to keep the example simple. But I really want both the attribute name and the attribute reference for each class and ancestor class. One of the answers below has me on a better track, I'll post some better code when I get it to work.

EDIT2 - 这是我想要的并且非常简洁.它基于 Eli 在下面的回答.

EDIT2 - This does what I want and is very succinct. It's based on Eli's answer below.

def get_attributes(type):

    attributes = set(type.__dict__.items())

    for type in type.__mro__:
        attributes.update(type.__dict__.items())

    return attributes

它返回属性名称和它们的引用.

It gives back both the attribute names and their references.

EDIT3 - 以下答案之一建议使用inspect.getmembers.这看起来非常有用,因为它就像 dict 一样,只是它也对祖先类进行操作.

EDIT3 - One of the answers below suggested using inspect.getmembers. This appears very useful because it's like dict only it operates on ancestor classes as well.

由于我试图做的大部分工作是查找标有特定描述符的属性,并包括祖先类,因此这里有一些代码可以帮助做到这一点,以防对任何人有所帮助:

Since a large part of what I was trying to do was find attributes marked with a particular descriptor, and include ancestors classes, here is some code that would help do that in case it helps anyone:

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


解决方案

您应该使用 python 的 inspect 模块来实现任何此类内省功能.

You should use python's inspect module for any such introspective capabilities.

.
.
>>> 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模块的信息.

Read more about the inspect module here.

相关文章