我正在阅读有关我们如何确保在 Python 中封装数据的内容。其中一篇博客说 “数据封装意味着,我们应该只能通过 getter 和 setter 访问私有(private)属性”
考虑以下来自 blog 的片段:
class Robot:
def __init__(self, name=None, build_year=None):
self.name = name
self.build_year = build_year
现在,如果我创建类的对象如下:
obj1=Robot()
obj1.name('Robo1")
obj1.build_year("1978")
目前,我可以直接访问属性,因为我已经将它们定义为公开的(没有 __notation)
现在为了确保数据封装,我需要将属性定义为私有(private) 使用 __ 表示法并通过 getter 和 setter 访问私有(private)属性。
所以新的类定义如下:
class Robot:
def __init__(self, name=None, build_year=2000):
self.__name = name
self.__build_year = build_year
def set_name(self, name):
self.__name = name
def get_name(self):
return self.__name
def set_build_year(self, by):
self.__build_year = by
def get_build_year(self):
return self.__build_year
现在我实例化类如下:
x = Robot("Marvin", 1979)
x.set_build_year(1993)
这样,我实现了数据封装,因为不再直接访问私有(private)数据成员,它们只能通过类方法访问。
Q1:我们为什么要做这个?我们在保护代码免受谁的侵害?谁是外面的世界?任何拥有源代码的人都可以根据自己的需要调整它,那么我们为什么要添加额外的方法(get/set)来修改/调整属性?
Q2:上面的例子是否考虑了数据封装?
请您参考如下方法:
数据封装比访问保护稍微更通用一些。 name 和 build_year 被类 Robot 封装,无论您如何定义属性。 Python 认为 getter 和 setter 只是访问或分配给底层属性是不必要的。
即使使用双下划线前缀也只是建议,更关心的是防止子类中的名称冲突。如果你真的想直接访问 __build_year 属性,你仍然可以使用
# Prefix attribute name with _Robot
x._Robot__build_year = 1993
Python 中更好的设计是使用属性,这会导致 Python 在直接定义属性时调用已定义的 getter 和/或 setter。例如:
class Robot(object):
def __init__(self, name, by):
self.name = name
self.build_year = by
@property
def name(self):
return self._name
@name.setter
def name(self, newname):
self._name = newname
@property
def build_year(self):
return self._build_year
@build_year.setter
def build_year(self, newby):
self._build_year = newby
你实际上不会如此简单地定义这些属性函数,但一个很大的好处是你可以从允许直接访问 name 属性开始,如果你以后决定应该有更多获取/设置值所涉及的逻辑,并且您想切换到属性,您可以在不影响现有代码的情况下这样做。代码如下
x = Robot("bob", 1993)
x.build_year = 1993
无论 x.build_year = 1993 是否直接分配给 build_year 或者它是否真的触发对属性 setter 的调用,都将工作相同。
