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

时间:2011-12-16 02:39:56

标签: python attributes

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

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

5 个答案:

答案 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('__')之类的内容来过滤此内容。