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

时间:2015-03-27 02:23:03

标签: python class python-3.x attributes

如果我有父类和子类,在询问属性时我会在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,在实例属性中找到j

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

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')

我想要的是描述符“无声地失败”,并且属性查找继续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(),但我不知道如何处理它。

2 个答案:

答案 0 :(得分:1)

两件事。

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

  2. 在查找过程中,您可以使用Parent

  3. 将属性提取工作委托给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)
    

答案 1 :(得分:1)

您可以按照以下方式在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)