如何从描述符向上委托 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
i
和 j
属性的请求按预期进入 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()
,但我不知道如何处理它。
两件事。
您需要在 _Attr
中存储对实际属性名称的引用,以便您可以在父查找中使用它。
查找时,可以将属性获取工作委托给Parent
class,用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)
如果我有一个父 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
i
和 j
属性的请求按预期进入 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()
,但我不知道如何处理它。
两件事。
您需要在
_Attr
中存储对实际属性名称的引用,以便您可以在父查找中使用它。查找时,可以将属性获取工作委托给
Parent
class,用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)