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 中的“公共”或“私有”属性?啥是最好的方法?的主要内容,如果未能解决你的问题,请参考以下文章