我来自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中不需要这个?


当前回答

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

其他回答

“在java中,我们学习了公共/私有/受保护变量”

“为什么在python中不需要这个?”

出于同样的原因,它在Java中不是必需的。

您可以自由使用或不使用private和protected。

作为Python和Java程序员,我发现私有和受保护是非常非常重要的设计概念。但实际上,在数万行Java和Python代码中,我从未真正使用过private或protected。

为什么不呢?

我的问题是"被谁保护?"

我团队中的其他程序员?他们有消息来源。当他们可以改变的时候,保护意味着什么?

其他团队的其他程序员?他们在同一家公司工作。只要打个电话,他们就能找到线人。

客户吗?这是一种雇佣式编程(通常)。客户机(通常)拥有代码。

那么,我到底在保护谁呢?

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

我是Python的新手但我有c#和JavaScript的背景。就特性而言,Python感觉像是两者的混合体。JavaScript在这方面也有问题,解决方法是创建一个闭包。这可以通过返回不同的对象来防止访问您不想公开的数据。

def print_msg(msg):
    # This is the outer enclosing function

    def printer():
        # This is the nested function
        print(msg)

    return printer  # returns the nested function


# Now let's try calling this function.
# Output: Hello
another = print_msg("Hello")
another()

https://www.programiz.com/python-programming/closure

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures#emulating_private_methods_with_closures

自从我开始开发一个想要发布的包以来,我一直在考虑私有类属性和方法(后续阅读中称为成员)。它背后的想法从来都不是不可能覆盖这些成员,而是对那些接触它们的人发出警告。我想到了一些可能有用的解决办法。第一个解决方案在我最喜欢的Python书籍之一《Fluent Python》中使用。


技术1的优点:

它不太可能被意外覆盖。 它很容易理解和实现。 它比实例属性的前双下划线更容易处理。

*在书中使用了哈希符号,但你也可以使用整数转换为字符串。在Python中,禁止使用class .1

class Technique1:

    def __init__(self, name, value):
        setattr(self, f'private#{name}', value)
        setattr(self, f'1{name}', value)

技术1的缺点:

但是,使用这种技术不容易保护方法。这是可能的。 属性查找只能通过getattr实现 仍然没有对用户发出警告


我遇到的另一个解决方案是编写__setattr__。优点:

它很容易实现和理解 它与方法一起工作 查找不受影响 用户得到一个警告或错误

class Demonstration:

    def __init__(self):
        self.a = 1

    def method(self):
        return None

    def __setattr__(self, name, value):
        if not getattr(self, name, None):
            super().__setattr__(name, value)
        else:
            raise ValueError(f'Already reserved name: {name}')

d = Demonstration()
#d.a = 2
d.method = None

缺点:

您仍然可以重写类 为了让变量不仅仅是常量,您需要映射允许的输入。 子类仍然可以覆盖方法


为了防止子类覆盖方法,你可以使用__init_subclass__:

class Demonstration:
    __protected = ['method']

    def method(self):
        return None

    def __init_subclass__(cls):
        protected_methods = Demonstration.__protected
        subclass_methods = dir(cls)
        for i in protected_methods:
            p = getattr(Demonstration,i)
            j = getattr(cls, i)
            if not p is j:
                raise ValueError(f'Protected method "{i}" was touched')

可以看到,有很多方法可以保护类成员,但不能保证用户不会覆盖它们。这应该能给你们一些启发。最后,您还可以使用元类,但这可能会带来新的危险。这里使用的技术也非常简单,你应该看看文档,你可以找到这个技术的有用特性,并根据你的需要定制它们。

下面是我处理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.