了解Python中的数据封装

fso*_*ety 1 python class object

我正在阅读我们如何确保python中的数据封装.博客中的一篇文章说"数据封装意味着,我们应该只能通过getter和setter访问私有属性"

请考虑以下来自博客的片段:

class Robot:
    def __init__(self, name=None, build_year=None):
        self.name = name
        self.build_year = build_year
Run Code Online (Sandbox Code Playgroud)

现在,如果我创建类的对象如下:

obj1=Robot()
obj1.name('Robo1")
obj1.build_year("1978")
Run Code Online (Sandbox Code Playgroud)

目前,我可以直接访问属性,因为我已将它们公开定义(没有__表示法)

现在为了确保数据封装,我需要使用__符号将属性定义为私有,并通过getter和setter访问私有属性.

所以新的类定义如下:

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    
Run Code Online (Sandbox Code Playgroud)

现在我将类实例化如下:

x = Robot("Marvin", 1979)
x.set_build_year(1993)
Run Code Online (Sandbox Code Playgroud)

这样,我就不再直接访问私有数据成员,而只能通过类方法访问它们.

Q1:我们为什么要这样做?我们是谁保护代码?谁是外部世界?拥有源代码的任何人都可以根据他们的要求调整它,那么为什么我们要添加额外的方法(获取/设置)来修改/调整属性?

Q2:上面的例子是否被考虑过了data encapsulation

che*_*ner 5

数据封装比访问保护略胜一筹.name并且无论您如何定义属性,build_year都由类封装Robot.Python采取的立场是,除了访问或分配给底层属性之外什么都不做的getter和setter是不必要的.

即使使用双下划线前缀也只是建议,并且更关心防止子类中的名称冲突.如果你真的想__build_year直接获取属性,你仍然可以使用

# Prefix attribute name with _Robot
x._Robot__build_year = 1993
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

您实际上不会如此简单地定义这些属性函数,但是一个很大的好处是您可以从允许直接访问name属性开始,如果您稍后决定在获取/设置值中需要更多逻辑并且您想要要切换到属性,您可以这样做而不会影响现有代码.代码就像

x = Robot("bob", 1993)
x.build_year = 1993
Run Code Online (Sandbox Code Playgroud)

无论是否直接x.build_year = 1993分配,build_year或者它是否真的触发了对属性设置器的调用,它都将工作相同.