题
我来自Java世界并阅读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中不需要这样做?
解决方案
这是文化。在Python中,您不会写入其他类的实例或类变量。在Java中,如果确实想要的话,没有什么能阻止你做同样的事情 - 毕竟,你总是可以编辑类本身的源来达到同样的效果。 Python放弃了安全性的假装并鼓励程序员负责。在实践中,这非常有效。
如果由于某种原因想要模拟私有变量,可以始终使用 __ 前缀。 0008 /“> PEP 8 。 Python破坏了像 __ foo
这样的变量的名称,这样它们就不容易被包含它们的类之外的代码看到(尽管你可以绕过它,如果你确定的话就像你可以绕过Java的保护,如果你工作的那样)。
按照相同的惯例, _
前缀意味着远离,即使您在技术上没有阻止这样做。您不会使用其他类的变量,它们看起来像 __ foo
或 _bar
。
其他提示
python中的私有变量或多或少是一个hack:解释器故意重命名变量。
class A:
def __init__(self):
self.__var = 123
def printVar(self):
print self.__var
现在,如果您尝试在类定义之外访问 __ var
,它将失败:
>>>x = A()
>>>x.__var # this will return error: "A has no attribute __var"
>>>x.printVar() # this gives back 123
但你可以轻松逃脱:
>>>x.__dict__ # this will show everything that is contained in object x
# which in this case is something like {'_A__var' : 123}
>>>x._A__var = 456 # you now know the masked name of private variables
>>>x.printVar() # this gives back 456
您可能知道OOP中的方法是这样调用的: x.printVar()=> A.printVar(x)
,如果 A.printVar()
可以访问 x
中的某个字段,也可以在外面访问该字段 A.printVar()
...毕竟,为可重用性创建了函数,没有赋予内部语句特殊的权力。
当涉及编译器时,游戏是不同的(隐私是编译器级别概念)。它知道使用访问控制修饰符的类定义,因此如果在编译时没有遵循规则,它就会出错
正如上面的许多评论所正确提到的,让我们不要忘记访问修饰符的主要目标:帮助代码用户理解应该更改的内容和不应该更改的内容。当你看到私人领域时,你不会乱用它。所以它主要是语法糖,可以通过_和__轻松实现。
"在java中,我们已经了解了公共/私人/受保护变量"
“为什么在python中不需要?”
出于同样的原因,它在Java中不是 required 。
您可以自由使用 - 或者不使用 private
和 protected
。
作为Python和Java程序员,我发现 private
和 protected
是非常非常重要的设计概念。但实际上,在数以万计的Java和Python系列中,我从来没有实际使用 private
或 protected
。
为什么不呢?
这是我的问题“受到谁保护?”
我团队中的其他程序员?他们有源头。受保护意味着什么时候可以改变它?
其他团队的其他程序员?他们在同一家公司工作。他们可以 - 通过电话 - 获取消息来源。
客户端?这是一种招聘工作(一般)。客户(通常)拥有代码。
那么,究竟是谁 - 我是在保护它吗?
下划线约定中有一些私有变量。
In [5]: class Test(object):
...: def __private_method(self):
...: return "Boo"
...: def public_method(self):
...: return self.__private_method()
...:
In [6]: x = Test()
In [7]: x.public_method()
Out[7]: 'Boo'
In [8]: x.__private_method()
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-8-fa17ce05d8bc> in <module>()
----> 1 x.__private_method()
AttributeError: 'Test' object has no attribute '__private_method'
有一些微妙的差异,但为了编程模式的思想纯度,它足够好。
有些@private装饰器的例子更贴切地实现了这个概念,但YMMV。可以说,人们也可以写一个使用meta
的类定义Python对私有标识符的支持有限,通过自动将类名添加到以两个下划线开头的任何标识符的功能。在大多数情况下,这对程序员来说是透明的,但实际效果是任何以这种方式命名的变量都可以用作私有变量。
有关详情,请参阅此处。
通常,与其他语言相比,Python的面向对象实现有点原始。但实际上,我喜欢这个。这是一个概念上非常简单的实现,非常适合语言的动态风格。
如前所述,您可以通过在变量或方法前加下划线来指示变量或方法是私有的。如果您觉得这不够,可以随时使用属性
装饰器。这是一个例子:
class Foo:
def __init__(self, bar):
self._bar = bar
@property
def bar(self):
"""Getter for '_bar'."""
return self._bar
这样,引用 bar
的某人或某事实际上是引用 bar
函数的返回值而不是变量本身,因此可以访问但不能访问改变。但是,如果有人真的想要,他们可以简单地使用 _bar
并为其分配一个新值。正如已经反复说过的那样,没有万无一失的方法来阻止某人访问你想要隐藏的变量和方法。但是,使用属性
是您可以发送的最清晰的消息,即不要编辑变量。 property
也可用于更复杂的getter / setter / deleter访问路径,如下所述: https://docs.python.org/3/library/functions.html#property
我唯一一次使用私有变量是在写入或读取变量时我需要做其他事情,因此我需要强制使用setter和/或getter。
如上所述,这又归于文化。我一直致力于读取和编写其他类变量的项目是免费的。当一个实现被弃用时,需要花费更长的时间来识别使用该功能的所有代码路径。当强制使用setter和getter时,可以很容易地编写一个debug语句来识别已经调用过的方法以及调用它的代码路径。
当你所在的项目中,任何人都可以编写扩展程序,通知用户有关在几个版本中消失的弃用方法因此对于在升级时将模块破坏保持在最低限度至关重要。
所以我的答案是;如果您和您的同事维护一个简单的代码集,那么保护类变量并不总是必要的。如果您正在编写可扩展系统,则必须使用代码对所有扩展需要捕获的核心进行更改。
私人和受保护的概念非常重要。但是python - 只是一个用于原型设计和快速开发的工具,可用于开发的资源有限,这就是为什么python中的一些保护级别不是那么严格的原因。您可以使用“__”在类成员中,它工作正常,但看起来不够好 - 每次访问此类字段都包含这些字符。
另外,你可以注意到python OOP概念并不完美,smaltalk或ruby更接近纯OOP概念。甚至C#或Java也更接近。
Python是非常好的工具。但它是简化的OOP语言。在语法和概念上简化。 python存在的主要目标是为开发人员提供以非常快的方式编写具有高抽象级别的易读代码的可能性。
对不起家伙们“复活”线程,但是,我希望这会对某人有所帮助:
在Python3中,如果你只想“封装”类的属性,就像在Java中一样,你可以像这样做:
class Simple:
def __init__(self, str):
print("inside the simple constructor")
self.__s = str
def show(self):
print(self.__s)
def showMsg(self, msg):
print(msg + ':', self.show())
要实例化,请执行以下操作:
ss = Simple("lol")
ss.show()
请注意: print(ss .__ s)
会引发错误。
实际上,Python3将模糊全局属性名称。把它变成“私人”的属性,就像在Java中一样。属性的名称仍然是全局的,但是以不可访问的方式,如其他语言中的私有属性。
但不要害怕它。没关系。它也完成了这项工作。 ;)
Python没有像C ++或Java那样的私有变量。如果需要,您可以随时访问任何成员变量。但是,在Python中不需要私有变量,因为在Python中暴露类成员变量并不坏。如果您需要封装成员变量,可以使用“@ property”进行封装。稍后在不破坏现有客户端代码的情况下。
在python中,单个下划线“_”用于表示方法或变量不被视为类的公共API的一部分,并且api的这一部分可能在不同版本之间发生变化。您可以使用这些方法/变量,但如果您使用此类的较新版本,则代码可能会中断。
双下划线“__”并不意味着“私人变量”。您可以使用它来定义“类本地”变量。并且子类不能轻易覆盖它。它会破坏变量名称。
例如:
class A(object):
def __init__(self):
self.__foobar = None # will be automatically mangled to self._A__foobar
class B(A):
def __init__(self):
self.__foobar = 1 # will be automatically mangled to self._B__foobar
自我.__ foobar的名字在A级自动被修改为self._A__foobar。在B组中,它被自我毁坏了._B__foobar。因此,每个子类都可以定义自己的变量__foobar,而不会覆盖其父变量。但没有什么能阻止您访问以双下划线开头的变量。但是,名称修改会阻止您顺便调用此变量/方法。
我强烈建议观看Raymond Hettingers谈论“Pythons类开发工具包”。来自Pycon 2013(应该在Youtube上提供),它提供了一个很好的例子,说明为什么以及如何使用@property和“__”-instance变量。