python 嵌套属性封装
python nested attributes encapsulation
我对 python 中的封装嵌套属性有一些疑问。让我们假设几个 classes:
这里我们有一个主要的 class (DataWrapper),它包括另外两个 classes:InnerWrapper1 和 InnerWrapper2。两个内部包装器都包含两个属性。
class DataWrapper(object):
@property
def inner_wrapper1(self):
return self.__inner_wrapper1
@inner_wrapper1.setter
def inner_wrapper1(self, value):
self.__inner_wrapper1 = value
@property
def inner_wrapper2(self):
return self.__inner_wrapper2
@inner_wrapper2.setter
def inner_wrapper2(self, value):
self.__inner_wrapper2 = value
class InnerWrapper1(object):
@property
def property1(self):
return self.__property1
@property1.setter
def property1(self, value):
self.__property1 = value
@property
def property2(self):
return self.__property2
@property2.setter
def property2(self, value):
self.__property2 = value
class InnerWrapper2(object):
@property
def property3(self):
return self.__property3
@property3.setter
def property3(self, value):
self.__property3 = value
@property
def property4(self):
return self.__property4
@property4.setter
def property4(self, value):
self.__property4 = value
是否有可能以某种方式覆盖 getattr 和 setattr 方法以实现下面的封装?我想要实现的是从顶部访问那些嵌套属性 class- DataWrapper.
data_wrapper = DataWrapper()
data_wrapper.property1 = "abc"
...
var = data_wrapper.property2
...
我首先想到的是在 getattr 中执行 hasattr,但这给出了最大递归深度...
完整代码如下:
class DataWrapper(object):
def __init__(self):
self.inner_wrapper1 = InnerWrapper1()
self.inner_wrapper2 = InnerWrapper2()
@property
def inner_wrapper1(self):
return self.__inner_wrapper1
@inner_wrapper1.setter
def inner_wrapper1(self, value):
self.__inner_wrapper1 = value
@property
def inner_wrapper2(self):
return self.__inner_wrapper2
@inner_wrapper2.setter
def inner_wrapper2(self, value):
self.__inner_wrapper2 = value
def __setattr__(self, attribute, value):
#if attribute in {'innerwrapper1', 'innerwrapper2'}:
if attribute in ['inner_wrapper1', 'inner_wrapper2']:
return super(DataWrapper, self).__setattr__(attribute, value)
if hasattr(self.inner_wrapper1, attribute):
return setattr(self.inner_wrapper1, attribute, value)
elif hasattr(self.inner_wrapper2, attribute):
return setattr(self.inner_wrapper2, attribute, value)
def __getattr__(self, attribute):
try:
return getattr(self.inner_wrapper1, attribute)
except AttributeError: pass
try:
return getattr(self.inner_wrapper2, attribute)
except AttributeError: pass
class InnerWrapper1(object):
@property
def property1(self):
return self.__property1
@property1.setter
def property1(self, value):
self.__property1 = value
@property
def property2(self):
return self.__property2
@property2.setter
def property2(self, value):
self.__property2 = value
class InnerWrapper2(object):
@property
def property3(self):
return self.__property3
@property3.setter
def property3(self, value):
self.__property3 = value
@property
def property4(self):
return self.__property4
@property4.setter
def property4(self, value):
self.__property4 = value
def main():
data_wrapper = DataWrapper()
data_wrapper.property1 = "abc"
if __name__ == "__main__":
main()
你得到一个无限递归错误,因为你忘了在你的 __init__
方法中设置 inner_wrapper1
和 inner_wrapper2
属性。
当你这样做时:
self.inner_wrapper1 = InnerWrapper()
Python 将 也 使用您的 __setattr__
方法。然后这会尝试使用尚不存在的 self.inner_wrapper1
,因此调用 __getattr__
,它会尝试使用尚不存在的 self.inner_wrapper1
,然后您进入无限递归循环。
在__setattr__
中将属性设置委托给超类:
def __setattr__(self, attribute, value):
if attribute in {'innerwrapper1', 'innerwrapper2'}:
return super(DataWrapper, self).__setattr__(attribute, value)
if hasattr(self.inner_wrapper1, attribute):
return setattr(self.inner_wrapper1, attribute, value)
elif hasattr(self.inner_wrapper2, attribute):
return setattr(self.inner_wrapper2, attribute, value)
如果您对 'private' 属性使用单个前导下划线(因此 _innerwrapper1
和 _innerwrapper2
),您可以测试一下:
def __setattr__(self, attribute, value):
if attribute[0] == '_': # private attribute
return super(DataWrapper, self).__setattr__(attribute, value)
因此您不必对一整套名称进行硬编码。
由于您更新的完整脚本使用 __inner_wrapper1
和 __inner_wrapper2
作为实际的属性名称, 和 您正在使用属性,您必须调整您的 __setattr__
测试以查找 那些 名称。因为您使用的是双下划线名称,所以您需要针对 name mangling of such attributes:
进行调整
def __setattr__(self, attribute, value):
if attribute in {
'inner_wrapper1', 'inner_wrapper2',
'_DataWrapper__inner_wrapper1', '_DataWrapper__inner_wrapper2'}:
return super(DataWrapper, self).__setattr__(attribute, value)
除非您要继承 DataWrapper
并且必须保护您的属性不被意外覆盖,否则我会完全避免使用双下划线名称。在 Pythonic 代码中,您不必担心其他代码访问属性,没有真正私有属性的概念。
在这里使用属性也是矫枉过正;属性不会为您购买封装,在 Python 中,您只能使用它们来简化 API(用属性访问替换方法调用)。
请注意,hasattr()
InnerWrapper*
property*
属性的测试将 失败 因为您没有默认值:
>>> inner = InnerWrapper1()
>>> hasattr(inner, 'property1')
False
hasattr()
不测试属性,它只是尝试访问一个属性,如果 any 异常被引发它 returns False
:
>>> inner = InnerWrapper1()
>>> hasattr(inner, 'property1')
False
>>> inner.property1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 43, in property1
AttributeError: 'InnerWrapper1' object has no attribute '_InnerWrapper1__property1'
>>> inner.property1 = 'foo'
>>> inner.property1
'foo'
>>> hasattr(inner, 'property1')
True
通过删除所有 @property
对象,您可以大大简化此过程:
class DataWrapper(object):
def __init__(self):
self._inner_wrapper1 = InnerWrapper1()
self._inner_wrapper2 = InnerWrapper2()
def __setattr__(self, attribute, value):
if attribute[0] == '_':
return super(DataWrapper, self).__setattr__(attribute, value)
if hasattr(self._inner_wrapper1, attribute):
return setattr(self._inner_wrapper1, attribute, value)
elif hasattr(self._inner_wrapper2, attribute):
return setattr(self._inner_wrapper2, attribute, value)
def __getattr__(self, attribute):
try:
return getattr(self._inner_wrapper1, attribute)
except AttributeError: pass
return getattr(self._inner_wrapper2, attribute)
class InnerWrapper1(object):
property1 = None
property2 = None
class InnerWrapper2(object):
property3 = None
property4 = None
我对 python 中的封装嵌套属性有一些疑问。让我们假设几个 classes: 这里我们有一个主要的 class (DataWrapper),它包括另外两个 classes:InnerWrapper1 和 InnerWrapper2。两个内部包装器都包含两个属性。
class DataWrapper(object):
@property
def inner_wrapper1(self):
return self.__inner_wrapper1
@inner_wrapper1.setter
def inner_wrapper1(self, value):
self.__inner_wrapper1 = value
@property
def inner_wrapper2(self):
return self.__inner_wrapper2
@inner_wrapper2.setter
def inner_wrapper2(self, value):
self.__inner_wrapper2 = value
class InnerWrapper1(object):
@property
def property1(self):
return self.__property1
@property1.setter
def property1(self, value):
self.__property1 = value
@property
def property2(self):
return self.__property2
@property2.setter
def property2(self, value):
self.__property2 = value
class InnerWrapper2(object):
@property
def property3(self):
return self.__property3
@property3.setter
def property3(self, value):
self.__property3 = value
@property
def property4(self):
return self.__property4
@property4.setter
def property4(self, value):
self.__property4 = value
是否有可能以某种方式覆盖 getattr 和 setattr 方法以实现下面的封装?我想要实现的是从顶部访问那些嵌套属性 class- DataWrapper.
data_wrapper = DataWrapper()
data_wrapper.property1 = "abc"
...
var = data_wrapper.property2
...
我首先想到的是在 getattr 中执行 hasattr,但这给出了最大递归深度...
完整代码如下:
class DataWrapper(object):
def __init__(self):
self.inner_wrapper1 = InnerWrapper1()
self.inner_wrapper2 = InnerWrapper2()
@property
def inner_wrapper1(self):
return self.__inner_wrapper1
@inner_wrapper1.setter
def inner_wrapper1(self, value):
self.__inner_wrapper1 = value
@property
def inner_wrapper2(self):
return self.__inner_wrapper2
@inner_wrapper2.setter
def inner_wrapper2(self, value):
self.__inner_wrapper2 = value
def __setattr__(self, attribute, value):
#if attribute in {'innerwrapper1', 'innerwrapper2'}:
if attribute in ['inner_wrapper1', 'inner_wrapper2']:
return super(DataWrapper, self).__setattr__(attribute, value)
if hasattr(self.inner_wrapper1, attribute):
return setattr(self.inner_wrapper1, attribute, value)
elif hasattr(self.inner_wrapper2, attribute):
return setattr(self.inner_wrapper2, attribute, value)
def __getattr__(self, attribute):
try:
return getattr(self.inner_wrapper1, attribute)
except AttributeError: pass
try:
return getattr(self.inner_wrapper2, attribute)
except AttributeError: pass
class InnerWrapper1(object):
@property
def property1(self):
return self.__property1
@property1.setter
def property1(self, value):
self.__property1 = value
@property
def property2(self):
return self.__property2
@property2.setter
def property2(self, value):
self.__property2 = value
class InnerWrapper2(object):
@property
def property3(self):
return self.__property3
@property3.setter
def property3(self, value):
self.__property3 = value
@property
def property4(self):
return self.__property4
@property4.setter
def property4(self, value):
self.__property4 = value
def main():
data_wrapper = DataWrapper()
data_wrapper.property1 = "abc"
if __name__ == "__main__":
main()
你得到一个无限递归错误,因为你忘了在你的 __init__
方法中设置 inner_wrapper1
和 inner_wrapper2
属性。
当你这样做时:
self.inner_wrapper1 = InnerWrapper()
Python 将 也 使用您的 __setattr__
方法。然后这会尝试使用尚不存在的 self.inner_wrapper1
,因此调用 __getattr__
,它会尝试使用尚不存在的 self.inner_wrapper1
,然后您进入无限递归循环。
在__setattr__
中将属性设置委托给超类:
def __setattr__(self, attribute, value):
if attribute in {'innerwrapper1', 'innerwrapper2'}:
return super(DataWrapper, self).__setattr__(attribute, value)
if hasattr(self.inner_wrapper1, attribute):
return setattr(self.inner_wrapper1, attribute, value)
elif hasattr(self.inner_wrapper2, attribute):
return setattr(self.inner_wrapper2, attribute, value)
如果您对 'private' 属性使用单个前导下划线(因此 _innerwrapper1
和 _innerwrapper2
),您可以测试一下:
def __setattr__(self, attribute, value):
if attribute[0] == '_': # private attribute
return super(DataWrapper, self).__setattr__(attribute, value)
因此您不必对一整套名称进行硬编码。
由于您更新的完整脚本使用 __inner_wrapper1
和 __inner_wrapper2
作为实际的属性名称, 和 您正在使用属性,您必须调整您的 __setattr__
测试以查找 那些 名称。因为您使用的是双下划线名称,所以您需要针对 name mangling of such attributes:
def __setattr__(self, attribute, value):
if attribute in {
'inner_wrapper1', 'inner_wrapper2',
'_DataWrapper__inner_wrapper1', '_DataWrapper__inner_wrapper2'}:
return super(DataWrapper, self).__setattr__(attribute, value)
除非您要继承 DataWrapper
并且必须保护您的属性不被意外覆盖,否则我会完全避免使用双下划线名称。在 Pythonic 代码中,您不必担心其他代码访问属性,没有真正私有属性的概念。
在这里使用属性也是矫枉过正;属性不会为您购买封装,在 Python 中,您只能使用它们来简化 API(用属性访问替换方法调用)。
请注意,hasattr()
InnerWrapper*
property*
属性的测试将 失败 因为您没有默认值:
>>> inner = InnerWrapper1()
>>> hasattr(inner, 'property1')
False
hasattr()
不测试属性,它只是尝试访问一个属性,如果 any 异常被引发它 returns False
:
>>> inner = InnerWrapper1()
>>> hasattr(inner, 'property1')
False
>>> inner.property1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 43, in property1
AttributeError: 'InnerWrapper1' object has no attribute '_InnerWrapper1__property1'
>>> inner.property1 = 'foo'
>>> inner.property1
'foo'
>>> hasattr(inner, 'property1')
True
通过删除所有 @property
对象,您可以大大简化此过程:
class DataWrapper(object):
def __init__(self):
self._inner_wrapper1 = InnerWrapper1()
self._inner_wrapper2 = InnerWrapper2()
def __setattr__(self, attribute, value):
if attribute[0] == '_':
return super(DataWrapper, self).__setattr__(attribute, value)
if hasattr(self._inner_wrapper1, attribute):
return setattr(self._inner_wrapper1, attribute, value)
elif hasattr(self._inner_wrapper2, attribute):
return setattr(self._inner_wrapper2, attribute, value)
def __getattr__(self, attribute):
try:
return getattr(self._inner_wrapper1, attribute)
except AttributeError: pass
return getattr(self._inner_wrapper2, attribute)
class InnerWrapper1(object):
property1 = None
property2 = None
class InnerWrapper2(object):
property3 = None
property4 = None