我来自Java世界,正在阅读Bruce Eckels的《Python 3 Patterns, Recipes and idiom》。

在阅读有关类的内容时,会继续说在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中不需要这个?


当前回答

关于源代码(更改访问权限,从而绕过Java或c++等语言封装):

您并不总是拥有源代码,即使您拥有源代码,这些源代码也由一个只允许特定程序员访问源代码的系统管理(在专业上下文中)。通常,每个程序员都负责某些类,因此知道自己能做什么,不能做什么。源代码管理器还锁定正在修改的源代码,当然,还管理程序员的访问权限。

所以根据经验,我更相信软件而不是人。所以约定很好,但多重保护更好,比如访问管理(真正的私有变量)+源代码管理。

其他回答

正如上面许多评论所正确提到的,让我们不要忘记Access Modifiers的主要目标:帮助代码的用户理解应该改变什么,不应该改变什么。当你看到一个私有的字段时,你不要乱摆弄它。所以它主要是语法糖,这在Python中很容易通过_和__实现。

在下划线约定中有一种私有变量的变体。

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 decorator的例子更紧密地实现了这个概念,但你的情况可能会有所不同。可以说,也可以编写使用meta的类定义。

私有变量在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()之外访问…毕竟,函数是为可重用性而创建的,其中的语句并没有任何特殊的功能。

下面是我处理Python 3类字段的方法:

class MyClass:
    def __init__(self, public_read_variable, private_variable):
        self.public_read_variable_ = public_read_variable
        self.__private_variable = private_variable

我只在MyClass方法中使用两个下划线访问__private_variable。

我用一个下划线对public_read_variable_进行读访问 在类之外,但从不修改变量:

my_class = MyClass("public", "private")
print(my_class.public_read_variable_) # OK
my_class.public_read_variable_ = 'another value' # NOT OK, don't do that.

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的名称自动被破坏为self。在类B中,它被破坏为self._B__foobar。因此,每个子类都可以定义自己的变量__foobar,而不重写其父变量。但是没有什么可以阻止您访问以双下划线开头的变量。但是,name mangling阻止你偶然调用这些变量/方法。

我强烈建议你观看Raymond Hettinger的Python类开发工具包,它提供了一个很好的例子,为什么以及如何使用@property和“__”-实例变量。

如果您已经公开了公共变量,并且需要封装它们,那么可以使用@property。因此,您可以从最简单的解决方案开始。你可以让成员变量为public,除非你有具体的理由不这样做。这里有一个例子:

class Distance:
    def __init__(self, meter):
        self.meter = meter


d = Distance(1.0)
print(d.meter)
# prints 1.0

class Distance:
    def __init__(self, meter):
        # Customer request: Distances must be stored in millimeters.
        # Public available internals must be changed.
        # This would break client code in C++.
        # This is why you never expose public variables in C++ or Java.
        # However, this is Python.
        self.millimeter = meter * 1000

    # In Python we have @property to the rescue.
    @property
    def meter(self):
        return self.millimeter *0.001

    @meter.setter
    def meter(self, value):
        self.millimeter = value * 1000

d = Distance(1.0)
print(d.meter)
# prints 1.0