Does Python have “private” variables in classes?

2018-12-31 06:04发布

I'm coming from the Java world and reading Bruce Eckels' Python 3 Patterns, Recipes and Idioms.

While reading about classes, it goes on to say that in Python there is no need to declare instance variables. You just use them in the constructor, and boom, they are there.

So for example:

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())

If that’s true, then any object of class Simple can just change the value of variable s outside of the class.

For example:

if __name__ == "__main__":
    x = Simple("constructor argument")
    x.s = "test15" # this changes the value
    x.show()
    x.showMsg("A message")

In Java, we have been taught about public/private/protected variables. Those keywords make sense because at times you want variables in a class to which no one outside the class has access to.

Why is that not required in Python?

11条回答
何处买醉
2楼-- · 2018-12-31 06:09

It's cultural. In Python, you don't write to other classes' instance or class variables. In Java, nothing prevents you from doing the same if you really want to - after all, you can always edit the source of the class itself to achieve the same effect. Python drops that pretence of security and encourages programmers to be responsible. In practice, this works very nicely.

If you want to emulate private variables for some reason, you can always use the __ prefix from PEP 8. Python mangles the names of variables like __foo so that they're not easily visible to code outside the class that contains them (although you can get around it if you're determined enough, just like you can get around Java's protections if you work at it).

By the same convention, the _ prefix means stay away even if you're not technically prevented from doing so. You don't play around with another class's variables that look like __foo or _bar.

查看更多
余生请多指教
3楼-- · 2018-12-31 06:11

Python has limited support for private identifiers, through a feature that automatically prepends the class name to any identifiers starting with two underscores. This is transparent to the programmer, for the most part, but the net effect is that any variables named this way can be used as private variables.

See here for more on that.

In general, Python's implementation of object orientation is a bit primitive compared to other languages. But I enjoy this, actually. It's a very conceptually simple implementation and fits well with the dynamic style of the language.

查看更多
情到深处是孤独
4楼-- · 2018-12-31 06:14

As correctly mentioned by many of the comments above, let's not forget the main goal of Access Modifiers: To help users of code understand what is supposed to change and what is supposed not to. When you see a private field you don't mess around with it. So it's mostly syntactic sugar which is easily achieved in Python by the _ and __.

查看更多
高级女魔头
5楼-- · 2018-12-31 06:16

Private variables in python is more or less a hack: the interpreter intentionally renames the variable.

class A:
    def __init__(self):
        self.__var = 123
    def printVar(self):
        print self.__var

Now, if you try to access __var outside the class definition, it will fail:

 >>>x = A()
 >>>x.__var # this will return error: "A has no attribute __var"

 >>>x.printVar() # this gives back 123

But you can easily get away with this:

 >>>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

You probably know that methods in OOP are invoked like this: x.printVar() => A.printVar(x), if A.printVar() can access some field in x, this field can also be accessed outside A.printVar()...after all, functions are created for reusability, there is no special power given to the statements inside.

The game is different when there is a compiler involved (privacy is a compiler level concept). It know about class definition with access control modifiers so it can error out if the rules are not being followed at compile time

查看更多
琉璃瓶的回忆
6楼-- · 2018-12-31 06:18

As mentioned earlier, you can indicate that a variable or method is private by prefixing it with an underscore. If you don't feel like this is enough, you can always use the property decorator. Here's an example:

class Foo:

    def __init__(self, bar):
        self._bar = bar

    @property
    def bar(self):
        """Getter for '_bar'."""
        return self._bar

This way, someone or something that references bar is actually referencing the return value of the bar function rather than the variable itself, and therefore it can be accessed but not changed. However, if someone really wanted to, they could simply use _bar and assign a new value to it. There is no surefire way to prevent someone from accessing variables and methods that you wish to hide, as has been said repeatedly. However, using property is the clearest message you can send that a variable is not to be edited. property can also be used for more complex getter/setter/deleter access paths, as explained here: https://docs.python.org/3/library/functions.html#property

查看更多
姐姐魅力值爆表
7楼-- · 2018-12-31 06:18

Python does not have any private variables like C++ or Java does. You could access any member variable at any time if wanted, too. However, you don't need private variables in Python, because in Python it is not bad to expose your classes member variables. If you have the need to encapsulate a member variable, you can do this by using "@property" later on without breaking existing client code.

In python the single underscore "_" is used to indicate, that a method or variable is not considered as part of the public api of a class and that this part of the api could change between different versions. You can use these methods/variables, but your code could break, if you use a newer version of this class.

The double underscore "__" does not mean a "private variable". You use it to define variables which are "class local" and which can not be easily overidden by subclasses. It mangles the variables name.

For example:

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

self.__foobar's name is automatically mangled to self._A__foobar in class A. In class B it is mangled to self._B__foobar. So every subclass can define its own variable __foobar without overriding its parents variable(s). But nothing prevents you from accessing variables beginning with double underscores. However, name-mangling prevents you from calling this variables /methods incidentally.

I strongly recommend to watch Raymond Hettingers talk "Pythons class development toolkit" from Pycon 2013 (should be available on Youtube), which gives a good example why and how you should use @property and "__"-instance variables.

查看更多
登录 后发表回答