如何从描述符向上委托 MRO 链的属性请求

How to delegate an attribute request up the MRO chain from a descriptor

如果我有一个父 class 和一个子 class,我在请求属性时在 Python 中得到以下行为:

class Parent():
    i = 1

class Child(Parent):
    def __init__(self, j = 2):
        self.j = j

ch = Child()
print(ch.i, ch.j) # 1 2

ij 属性的请求按预期进入 MRO 链; i 在父 class 属性中找到,j 在实例属性中找到。

现在,如果我添加通用描述符并替换 Child 中的 j 属性,则会发生这种情况:

class _Attr():
    def __init__(self, attr_name):
        self.attr_name = '_' + attr_name
    def __get__(self, instance, klass):
        return getattr(instance, self.attr_name)

class Child(Parent):
    j = _Attr('j')
    def __init__(self, j = 2):
        self._j = j

ch = Child()
print(ch.i, ch.j) # 1 2

到目前为止,还不错。

然而,使用上面的描述符,如果我们做这样的事情:

class Child(Parent):
    j = _Attr('j')
    i = _Attr('i')
    def __init__(self, j = 2):
        self._j = j

ch = Child()
print(ch.i, ch.j) # AttributeError: 'Ch" object has no attribute '_i'

此错误是由于属性查找失败造成的:

return getattr(ch, '_i')

我想要的是 "fail silently" 的描述符和继续 MRO 链的属性查找。我不确定该怎么做。

我试过这个:

class _Attr():
    def __init__(self, attr_name):
        self.attr_name = '_' + attr_name
    def __get__(self, instance, klass):
        result = getattr(instance, self.attr_name, None)
        if result == None:
            return NotImplemented
        else:
            return result

但这并不能解决问题。我怎样才能得到我想要的行为?我觉得在这种情况下我需要以某种方式使用 super(),但我不知道如何处理它。

两件事。

  1. 您需要在 _Attr 中存储对实际属性名称的引用,以便您可以在父查找中使用它。

  2. 查找时,可以将属性获取工作委托给Parentclass,用super(klass, instance)

所以你的 _Attr 看起来像这样

class _Attr():

    def __init__(self, attr_name):
        self._original_name = attr_name
        self.attr_name = '_' + attr_name

    def __get__(self, instance, klass):
        if hasattr(instance, self.attr_name):
            return getattr(instance, self.attr_name)
        return getattr(super(klass, instance), self._original_name)

您可以按如下方式在 init 方法中设置 _i。

class Child(P):
    j = _Attr('j')
    i = _Attr('i')
    def __init__(self, j = 2):
        self._j = j
        self._i = P.i

ch = Child()
print(ch._i, ch._j)