首页 文章

Python中的“私有”属性属性

提问于
浏览
7

我对Python比较陌生,所以我希望我没有错过任何东西,但是这里......

我正在尝试编写一个Python模块,并且我想创建一个具有“私有”属性的类,该属性可以(或者可能“应该”)仅通过模块中的一个或多个函数进行修改 . 这是为了使模块更加健壮,因为在这些函数之外设置此属性可能会导致不必要的行为 . 例如,我可能有:

  • 存储散点图的x和y值的类, Data

  • 从文件中读取x和y值并将其存储在类中的函数 read()

  • 绘制它们的函数, plot()

在这种情况下,我更喜欢用户不能做这样的事情:

data = Data()
read("file.csv", data)
data.x = [0, 3, 2, 6, 1]
plot(data)

我意识到在名称中添加一个前导下划线表示用户不应更改该属性,即重命名为 _x 并添加属性装饰器,以便用户可以访问该值而不会感到内疚 . 但是,如果我想添加一个setter属性呢:

class Data(object):
    _x = []
    _y = []
    @property
    def x(self):
        return self._x
    @x.setter
    def x(self, value):
        # Do something with value
        self._x = value

我现在处于与以前相同的位置 - 用户无法再直接访问属性 _x ,但他们仍然可以使用以下方式设置它:

data.x = [0, 3, 2, 6, 1]

理想情况下,我将属性函数定义重命名为 _x() ,但这会导致对 self._x 实际意味着什么的混淆(取决于它们被声明的顺序,这似乎导致setter被递归调用或者setter被忽略)赞成属性) .

我能想到的几个解决方案:

  • 在属性 __x 中添加一个双前导下划线,以便名称变得严重,并且不会与setter函数混淆 . 据我了解,这应该保留给类不希望与可能的子类共享的属性,所以我不确定这是否是合法用途 .

  • 重命名属性,例如 _x_stored . 虽然这完全解决了这个问题,但它使代码更难阅读并引入了命名约定问题 - 我会重命名哪些属性?只是那些相关的?只是有属性的?只是这个 class 里面的人?

以上哪种解决方案都适用?如果没有,有没有更好的方法来解决这个问题?

Edit

感谢到目前为止的回复 . 评论提出几点意见:

  • 我想保留setter属性给出的额外逻辑 - 上例中的 # Do something with value 部分 - 所以通过直接访问 self._x 来内部设置属性并不能解决问题 .

  • 删除setter属性并创建单独的函数 _set_x() 确实解决了问题,但它不是一个非常简洁的解决方案,因为它允许以两种不同的方式设置 _x - 通过调用该函数或直接访问 self._x . 我可能宁愿使用我上面提到的解决方案之一,因为尽管它们在类中混淆了命名约定,但它们在类之外的使用中至少是一致的,即它们都使用属性的语法糖 . 如果没有办法以更简洁的方式做到这一点,那么我想我只需要选择一个导致破坏最少的那个 .

1 回答

  • 4

    如果你想阻止用户更改属性,但希望它能够清楚地知道他们可以阅读它,我会使用 @property 而不提供setter,类似于你之前描述的:

    class Data(object):
        def __init__(self):
           self._x = []
           self._y = []
    
        @property 
        def x(self):
            return self._x
    
        @property 
        def y(self):
            return self._x
    

    我知道你提到"What if I wanted to add a setter to the property?",但我想我会反驳:如果你不希望你的客户能够设置属性,为什么要添加setter?在内部,您可以直接访问 self._x .

    对于直接访问 _x_y 的客户端,任何带有'_'前缀的变量在Python中都被理解为"private",因此您应该相信您的客户遵守该变量 . 如果他们不是自己的错 . 这种心态与许多其他语言(C,Java等)相反,其中保持数据私有被认为是非常重要的,但Python的文化在这方面是不同的 .

    Edit

    另外需要注意的是,由于您在这种特殊情况下的私有属性是可变的列表(不同于字符串或整数,它们是不可变的),客户端最终可能会意外地更改它们:

    >>> d = Data()
    >>> print d.x
    ['1', '2']
    >>> l = d.x
    >>> print l
    ['1', '2']
    >>> l.append("3")
    >>> print d.x
    ['1', '2', '3']  # Oops!
    

    如果你想避免这种情况,你需要你的 property 返回一份清单:

    @property
    def x(self):
        return list(self._x)
    

相关问题