如何在Python中访问超类的类属性?

Bre*_*els 5 python super superclass

看看下面的代码:

class A(object):
    defaults = {'a': 1}

    def __getattr__(self, name):
        print('A.__getattr__')
        return self.get_default(name)

    @classmethod
    def get_default(cls, name):
        # some debug output
        print('A.get_default({}) - {}'.format(name, cls))
        try:
            print(super(cls, cls).defaults) # as expected
        except AttributeError: #except for the base object class, of course
            pass

        # the actual function body
        try:
            return cls.defaults[name]
        except KeyError:
            return super(cls, cls).get_default(name) # infinite recursion
            #return cls.__mro__[1].get_default(name) # this works, though

class B(A):
    defaults = {'b': 2}

class C(B):
    defaults = {'c': 3}


c = C()
print('c.a =', c.a)
Run Code Online (Sandbox Code Playgroud)

我有一个类的层次结构,每个类都有自己的包含一些默认值的字典.如果类的实例没有特定属性,则应返回其默认值.如果当前类的defaults字典中不包含该属性的默认值,defaults则应搜索超类的字典.

我正在尝试使用递归类方法实现它get_default.不幸的是,该程序陷入无限递归.我的理解super()显然是缺乏的.通过访问__mro__,我可以让它正常工作,但我不确定这是一个正确的解决方案.

我觉得答案在本文的某个地方,但我还没有找到它.也许我需要求助于使用元类?

编辑:在我的应用程序中,__getattr__首先检查self.base.如果不是None,则需要从那里获取属性.仅在其他情况下,必须返回默认值.我可能会覆盖__getattribute__.这会是更好的解决方案吗?

编辑2:下面是我正在寻找的功能的扩展示例.它目前使用__mro__(unutbu的早期建议,而不是我原来的递归方法)实现.除非有人能提出更优雅的解决方案,否则我很高兴使用此实现.我希望这能解决问题.

class A(object):
    defaults = {'a': 1}

    def __init__(self, name, base=None):
        self.name = name
        self.base = base

    def __repr__(self):
        return self.name

    def __getattr__(self, name):
        print(" '{}' attribute not present in '{}'".format(name, self))
        if self.base is not None:
            print("  getting '{}' from base ({})".format(name, self.base))
            return getattr(self.base, name)
        else:
            print("  base = None; returning default value")
            return self.get_default(name)

    def get_default(self, name):
        for cls in self.__class__.__mro__:
            try:
                return cls.defaults[name]
            except KeyError:
                pass
        raise KeyError

class B(A):
    defaults = {'b': 2}

class C(B):
    defaults = {'c': 3}


c1 = C('c1')
c1.b = 55

print('c1.a = ...'); print('   ...', c1.a) # 1
print(); print('c1.b = ...'); print('   ...', c1.b) # 55
print(); print('c1.c = ...'); print('   ...', c1.c) # 3

c2 = C('c2', base=c1)
c2.c = 99

print(); print('c2.a = ...'); print('   ...', c2.a) # 1
print(); print('c2.b = ...'); print('   ...', c2.b) # 55
print(); print('c2.c = ...'); print('   ...', c2.c) # 99
Run Code Online (Sandbox Code Playgroud)

输出:

c1.a = ...
 'a' attribute not present in 'c1'
  base = None; returning default value
   ... 1

c1.b = ...
   ... 55

c1.c = ...
 'c' attribute not present in 'c1'
  base = None; returning default value
   ... 3

c2.a = ...
 'a' attribute not present in 'c2'
  getting 'a' from base (c1)
 'a' attribute not present in 'c1'
  base = None; returning default value
   ... 1

c2.b = ...
 'b' attribute not present in 'c2'
  getting 'b' from base (c1)
   ... 55

c2.c = ...
   ... 99
Run Code Online (Sandbox Code Playgroud)

Ton*_*uža 8

不是一个答案,而是一个观察:

对我来说这看起来过于工作,在寻找使用python魔法的借口时是一个常见的陷阱.

如果你可以defaults为一个类定义一个dict 而烦恼,为什么不直接定义属性呢?效果是一样的.

class A:
    a = 1

class B(A):
    b = 2

class C(B):
    c = 3


c = C()
print('c.a =', c.a)
Run Code Online (Sandbox Code Playgroud)

编辑:

至于回答这个问题,我可能会__getattribute__结合我的建议这样:

def __getattribute__(self, name):
    try:
        return object.__getattribute__(self.base, name)
    except AttributeError:
        return object.__getattribute__(self, name)
Run Code Online (Sandbox Code Playgroud)


Bre*_*els 0

问题第二次编辑中提出的解决方案仍然是唯一提供我的应用程序所需的一切的解决方案。虽然 unutbu 的代码可能更容易理解,但该__mro__解决方案在我看来提供了一些优势(请参阅评论)。