Python 中的“公共”或“私有”属性?啥是最好的方法?

Posted

技术标签:

【中文标题】Python 中的“公共”或“私有”属性?啥是最好的方法?【英文标题】:"public" or "private" attribute in Python ? What is the best way?Python 中的“公共”或“私有”属性?什么是最好的方法? 【发布时间】:2011-06-01 03:41:01 【问题描述】:

在 Python 中,我有以下示例类:

class Foo:
    self._attr = 0

    @property
    def attr(self):
        return self._attr

    @attr.setter
    def attr(self, value):
        self._attr = value

    @attr.deleter
    def attr(self):
        del self._attr

如您所见,我有一个简单的“私有”属性“_attr”和一个可以访问它的属性。有很多代码来声明一个简单的私有属性,我认为这样声明所有属性是不尊重“KISS”理念的。

那么,如果我不需要特定的 getter/setter/deleter,为什么不将我的所有属性声明为公共属性呢?

我的答案是: 因为封装原理(OOP)另有规定!

最好的方法是什么?

【问题讨论】:

“声明”是什么意思? Python 没有声明。因此,当您使用“声明”一词时,不清楚您的意思是什么。请更新您的问题以替换“声明”一词或定义您的意思。 @SLott 这很奇怪,因为例如chapter 9 of the Python tutorial 提到声明(例如在范围界定的上下文中)。它似乎是在常识中使用的(可以将其表述为“引入名称的语句、指令或表达式”)。 相关:***.com/questions/1641219/…. 【参考方案1】:

Python 没有公共或私有属性。所有代码都可以访问所有属性。

self.attr = 0 #Done

您的方法不会以任何方式将 _attr 设为私有,它只是有点混淆。

【讨论】:

我知道,但这不是我的问题。正如您在我的帖子中看到的那样,“私人”和“公共”用引号括起来。 那你的问题就没有意义了。这就像在不使用外部库的情况下询问如何在 C 中进行垃圾收集。该工具只是没有内置在语言中。 @SeyZ 如果你想要私有和公共以及所有爵士乐,你应该尝试 Java、C# 等,Python 不会为你完成它 尽管如此,使用前导下划线(或两个,取决于)是一个完善的 Python 约定,大致意思是,“嘿,不要直接访问它。我可能会更改它,删除它,或者在这个类的未来修订中以其他方式混淆它。”当然,这不是强制隐私;然而,它中被广泛使用,它被理解为一种传统隐私的形式。【参考方案2】:

通常,Python 代码会努力遵守Uniform Access Principle。具体来说,接受的方法是:

直接公开您的实例变量,例如允许foo.x = 0,而不是foo.set_x(0) 如果您需要将访问封装在方法中,无论出于何种原因,请使用@property,它保留了访问语义。也就是说,foo.x = 0 现在调用 foo.set_x(0)

这种方法的主要优点是调用者可以这样做:

foo.x += 1

即使代码可能真的在做:

foo.set_x(foo.get_x() + 1)

第一个语句的可读性大大提高。然而,通过属性,您可以(在开始时或稍后)添加通过第二种方法获得的访问控制。

还要注意,以单个下划线开头的实例变量通常是私有的。也就是说,下划线向其他开发人员发出信号,表明您认为该值是私有的,他们不应该直接混淆它;但是,语言中的任何内容都阻止他们直接使用它。

如果您使用双前导下划线(例如,__x),Python 会稍微混淆名称。但是,该变量仍然可以通过其混淆名称从类外部访问。这不是真正的私人。它只是有点……更不透明。并且有反对使用双下划线的有效论据;一方面,它会使调试变得更加困难。

【讨论】:

我如何与 Java 开发人员争论/讨论这不会使编程语言“更糟”或者它不只是“错过一个功能”? 我总是指出,与 Ruby、Python、Scala 和 C#(仅举几例)不同,Java 实际上更糟糕,因为它明显不遵守统一访问原则。因此,编码人员必须防御性地键入(或 IDE 必须生成)getter 和 setter,其中大多数不会增加任何实际价值——所有这些都意味着在维护类时需要处理更多无用的代码。 @BillalBEGUERADJ:你可以像这样:_ClassName__x。 See this question. @BrianClapper “未能遵守统一访问原则”——来自 JVM 世界,这是我们认为应该避免的,因为它使代码不那么“安全”。我的意思是,假设我们在类内部有一些可变属性,并且您想对其他人隐藏它的业务逻辑......不确定我是否想要公开。我很想知道为什么您认为在构建语言时遵守这一原则很重要。完全开放,给我一些参考【参考方案3】:

在 Python 中,除非您需要属性的特殊行为,否则无需将其隐藏在访问器方法后面。如果属性仅供内部使用,请在其前面加上下划线。

【讨论】:

【参考方案4】:

属性的好处是它们为您提供了一个非常酷的界面来使用。有时根据其他一些属性推导出一个属性是很方便的(即 BMI 由体重和身高定义)。界面的用户当然不必知道这一点。

我更喜欢这种方式,而不是像 Java ie 那样具有显式的 getter 和 setter。好多了:)

【讨论】:

【参考方案5】:

很简单,OOP 原则是错误的。为什么这是一个很长的讨论,会导致激烈的争吵,并且可能与本网站无关。 :-)

在 Python 中没有私有属性,您无法保护它们,这从来都不是真正的问题。所以不要。简单! :)

那么问题来了:你是否应该有一个前导下划线。在您在这里的示例中,您绝对不应该这样做。 Python 中的前导下划线是一种约定,表明某些东西是内部的,而不是 API 的一部分,您应该自担风险使用它。这显然不是这里的情况,但它是一种常见且有用的约定。

【讨论】:

"在 Python 中没有私有属性"。很多次我不得不解释封装与 Java 等其他编程语言的工作方式不同......【参考方案6】:

正如其他人所说,Python 中的私有属性只是一种约定。在绑定、修改或删除属性时,应使用property 语法进行特殊处理。 Python 的美妙之处在于,您可以从使用普通属性绑定开始,例如,self.attr = 0,如果以后您决定将 attr 的值限制为 0 <= attr <=100,则可以设置为 attr一个属性并定义一个方法来确保此条件为真,而无需更改任何用户代码。

【讨论】:

【参考方案7】:

要将属性设为私有,您只需 self.__attr

class Foo:
    self.__attr = 0

    @property
    def attr(self):
        return self._attr

    @attr.setter
    def attr(self, value):
        self._attr = value

    @attr.deleter
    def attr(self):
        del self._attr

【讨论】:

应该注意的是,从封装的角度来看,这实际上并没有使变量私有。所有这一切都执行了名称修改操作,该操作改变了访问变量的方式,但与来自 OOP 语言(如 Java)的真正私有变量的效果不同。只需访问名称为 mangled name 的变量即可访问它。【参考方案8】:

“dunder”(双下划线,__)前缀阻止访问属性,除非通过访问器。

class Foo():
    def __init__(self):
        self.__attr = 0

    @property
    def attr(self):  
        return self.__attr

    @attr.setter
    def attr(self, value):
        self.__attr = value

    @attr.deleter
    def attr(self):
        del self.__attr

一些例子:

>>> f = Foo()
>>> f.__attr                          # Not directly accessible.
Traceback (most recent call last):
    File "<input>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__attr'
>>> '__attr' in f.__dir__()           # Not listed by __dir__()
False
>>> f.__getattribute__('__attr')      # Not listed by __getattribute__()
Traceback (most recent call last):
    File "<input>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__attr'
>>> f.attr                            # Accessible by implemented getter.
0
>>> f.attr = 'Presto'                 # Can be set by implemented setter.
>>> f.attr
'Presto'
>>> f.__attr = 'Tricky?'              # Can we set it explicitly?
>>> f.attr                            # No. By doing that we have created a 
'Presto'                              # new but unrelated attribute, same name.

但是,您可以通过 name mangling (_classname__attribute) 访问此类属性,Python 在后台执行此操作:

>>> f._Foo__attr
0
>>> f.__getattribute__('_Foo__attr')
0

【讨论】:

但是...you can access the attribute through a special way: f.__getattribute__('_Foo__attr').【参考方案9】:

查看此链接:https://docs.python.org/2/tutorial/classes.html

9.6。私有变量和类局部引用

在 Python 中不存在只能从对象内部访问的“私有”实例变量。但是,大多数 Python 代码都遵循一个约定:前缀为下划线的名称(例如 _spam)应被视为 API 的非公共部分(无论是函数、方法还是数据成员) .它应被视为实现细节,如有更改,恕不另行通知。

由于类私有成员有一个有效的用例(即避免名称与子类定义的名称发生名称冲突),因此对这种称为名称修饰的机制的支持有限。 __spam 形式的任何标识符(至少两个前导下划线,最多一个尾随下划线)在文本上被替换为 _classname__spam,其中类名是当前类名,前导下划线被去除。只要它出现在类的定义中,就可以在不考虑标识符的句法位置的情况下完成这种修饰。

【讨论】:

以上是关于Python 中的“公共”或“私有”属性?啥是最好的方法?的主要内容,如果未能解决你的问题,请参考以下文章

用于存储简单数据的“公共静态”和“私有静态”之间的区别。啥是更好的? [复制]

Python中的公共和私有方法[重复]

界面中最好的类别或私有属性是啥

Python中的私有属性和私有方法

Swift 中的 iOS 私有/公共数据管理和存储

Python类私有方法的陷阱