我'm coming from the Java world and reading Bruce Eckels' Python 3模式,食谱和成语 .
在阅读关于类的内容时,接着说在Python中没有必要声明实例变量 . 你只需在构造函数中使用它们,然后繁荣,它们就在那里 .
例如:
class Simple:
def __init__(self, s):
print("inside the simple constructor")
self.s = s
def show(self):
print(self.s)
def showMsg(self, msg):
print(msg + ':', self.show())
如果这是真的,那么类 Simple
的任何对象都可以只改变类之外的变量 s
的值 .
例如:
if __name__ == "__main__":
x = Simple("constructor argument")
x.s = "test15" # this changes the value
x.show()
x.showMsg("A message")
在Java中,我们学习了有关公共/私有/受保护变量的知识 . 这些关键字是有意义的,因为有时你需要类中的变量,类外没有人可以访问 .
为什么Python中不需要这样做?
11 回答
python中的私有变量或多或少是一个hack:解释器故意重命名变量 .
现在,如果您尝试在类定义之外访问
__var
,它将失败:但你可以很容易地逃脱这个:
您可能知道OOP中的方法是这样调用的:
x.printVar() => A.printVar(x)
,如果A.printVar()
可以访问x
中的某个字段,也可以访问该字段 outsideA.printVar()
...毕竟,为可重用性创建了函数,没有给出特殊的权限里面的陈述 .当涉及编译器时,游戏会有所不同( privacy is a compiler level concept ) . 它知道带有访问控制修饰符的类定义,因此如果在编译时没有遵循规则,它就会出错
对不起那些“复活”线程的人,但是,我希望这会对某人有所帮助:
在Python3中,如果你只想“封装”类属性,就像在Java中一样,你可以像这样做:
要实例化这样做:
请注意:
print(ss.__s)
将抛出错误 .实际上,Python3将模糊全局属性名称 . 将其转换为“私有”属性,就像在Java中一样 . 属性的名称仍然是全局的,但是以不可访问的方式,如其他语言中的私有属性 .
但不要害怕它 . 没关系 . 它也完成了这项工作 . ;)
正如上面许多评论中正确提到的那样,让我们不要忘记访问修饰符的主要目标:帮助代码用户理解应该改变的内容和不应该发生的内容 . 当你看到私人领域时,你不会乱用它 . 所以它主要是语法糖,可以通过_和__在Python中轻松实现 .
下划线约定中有一些私有变量 .
有一些微妙的差异,但为了编程模式的思想纯度,它足够好 .
有一些@private装饰器的例子更接近实现这个概念,但是YMMV . 可以说,人们也可以写一个使用meta的类定义
“在java中,我们学过公共/私有/受保护的变量”
“为什么python中不需要这样做?”
出于同样的原因,Java中不需要它 .
您可以自由使用 - 或者不使用
private
和protected
.作为Python和Java程序员,我发现
private
和protected
是非常非常重要的设计概念 . 但实际上,在数万行Java和Python中,我从未真正使用过private
或protected
.为什么不?
这是我的问题“受到谁保护?”
我团队中的其他程序员?他们有源头 . 受保护意味着何时可以改变它?
其他团队的其他程序员?他们为同一家公司工作 . 他们可以 - 通过电话 - 获取消息来源 .
客户端?这是一种招聘工作(通常) . 客户(通常)拥有代码 .
那么,究竟是谁 - 我保护它免受伤害?
对 . 拒绝阅读API评论的精神分裂症反社会人士阻止 .
私人和受保护的概念非常重要 . 但是python - 只是一个原型设计和快速开发的工具,可用于开发的资源有限,这就是为什么python中的一些保护级别没有那么严格的原因 . 您可以在类成员中使用“__”,它可以正常工作,但看起来不够好 - 每次访问此类字段都包含这些字符 .
此外,你可以注意到python OOP概念并不完美,smaltalk或ruby更接近纯OOP概念 . 甚至C#或Java也更接近 .
Python是非常好的工具 . 但它是简化的OOP语言 . 在语法和概念上简化 . python存在的主要目标是为开发人员提供以非常快的方式编写具有高抽象级别的易读代码的可能性 .
Python对私有标识符的支持有限,通过一个自动将类名添加到以两个下划线开头的任何标识符的功能 . 在大多数情况下,这对程序员来说是透明的,但是净效果是任何以这种方式命名的变量都可以用作私有变量 .
有关详细信息,请参阅here .
通常,Python的面向对象实现与其他实现相比有点原始语言 . 但实际上,我喜欢这个 . 这是一个概念上非常简单的实现,非常适合语言的动态风格 .
Python没有像C或Java那样的私有变量 . 如果需要,您可以随时访问任何成员变量 . 但是,在Python中不需要私有变量,因为在Python中暴露类成员变量并不坏 . 如果您需要封装成员变量,则可以稍后使用“@property”执行此操作,而不会破坏现有的客户端代码 .
在python中,单个下划线“_”用于表示方法或变量不被视为类的公共API的一部分,并且api的这一部分可能在不同版本之间发生变化 . 您可以使用这些方法/变量,但如果您使用此类的较新版本,则代码可能会中断 .
双下划线“__”并不意味着“私人变量” . 您可以使用它来定义“类本地”的变量,这些变量不能被子类轻易覆盖 . 它会破坏变量名称 .
例如:
self .__ foobar的名字在A级自动被修改为self._A__foobar . 在B组中,它被自我毁坏了._B__foobar . 因此,每个子类都可以定义自己的变量__foobar,而不会覆盖其父变量 . 但没有什么能阻止您访问以双下划线开头的变量 . 但是,名称修改会阻止您顺便调用此变量/方法 .
我强烈建议观看Raymond Hettingers从Pycon 2013谈论“Pythons类开发工具包”(应该在Youtube上提供),这给出了一个很好的例子,为什么以及如何使用@property和“__” - 实例变量 .
它's cultural. In Python, you don' t写入其他类的实例或类变量 . 在Java中,如果你真的想要,没有什么能阻止你做同样的事情 - 毕竟,你总是可以编辑类本身的源来达到同样的效果 . Python放弃了安全性的假装并鼓励程序员负责 . 在实践中,这非常好用 .
如果由于某种原因想要模拟私有变量,可以始终使用PEP 8中的
__
前缀 . Python破坏了像__foo
这样的变量的名称,以便它们不容易被包含它们的类外的代码看到(尽管如果你足够坚定,你可以绕过它,就像你可以绕过Java的保护,如果你在它) .按照相同的惯例,
_
前缀表示 stay away even if you're not technically prevented from doing so . 你不是't play around with another class'的变量看起来像__foo
或_bar
.我唯一一次使用私有变量是在写入或读取变量时我需要做其他事情,因此我需要强制使用setter和/或getter .
如前所述,这再次涉及文化 . 我一直致力于读取和编写其他类变量的项目是免费的 . 当一个实现被弃用时,需要花费更长的时间来识别使用该功能的所有代码路径 . 当强制使用setter和getter时,可以很容易地编写一个debug语句来识别已经调用过的方法以及调用它的代码路径 .
当您处于任何人都可以编写扩展的项目时,通知用户有关在几个版本中消失的弃用方法因此对于在升级时将模块破坏保持在最低限度至关重要 .
所以我的答案是;如果您和您的同事维护一个简单的代码集,那么保护类变量并不总是必要的 . 如果您正在编写可扩展系统,则必须使用代码对所有扩展需要捕获的核心进行更改 .
如前所述,您可以通过在其前面添加下划线来指示变量或方法是私有的 . 如果您觉得这不够,可以随时使用
property
装饰器 . 这是一个例子:这样,引用
bar
的某人或某事实际上是引用bar
函数的返回值而不是变量本身,因此可以访问但不能更改它 . 但是,如果有人真的想要,他们可以简单地使用_bar
并为其分配一个新值 . 正如已经反复说过的那样,没有万无一失的方法来阻止某人访问你想要隐藏的变量和方法 . 但是,使用property
是您可以发送的最清晰的消息,即不要编辑变量 .property
也可以用于更复杂的getter / setter / deleter访问路径,如下所述:https://docs.python.org/3/library/functions.html#property