为什么 Python 是“私有的”? 方法实际上不是私有的?

发布于 07-04 15:33 字数 1820 浏览 14 评论 0原文

Python 使我们能够通过在名称前添加双下划线来在类中创建“私有”方法和变量,如下所示:__myPrivateMethod()。 那么,如何解释这

>>>> class MyClass:
...     def myPublicMethod(self):
...             print 'public method'
...     def __myPrivateMethod(self):
...             print 'this is private!!'
...
>>> obj = MyClass()

>>> obj.myPublicMethod()
public method

>>> obj.__myPrivateMethod()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: MyClass instance has no attribute '__myPrivateMethod'

>>> dir(obj)
['_MyClass__myPrivateMethod', '__doc__', '__module__', 'myPublicMethod']

>>> obj._MyClass__myPrivateMethod()
this is private!!

是怎么回事?

我将为那些不太明白的人解释一下。

>>> class MyClass:
...     def myPublicMethod(self):
...             print 'public method'
...     def __myPrivateMethod(self):
...             print 'this is private!!'
...
>>> obj = MyClass()

我创建一个具有公共方法和私有方法的类并实例化它。

接下来,我调用它的公共方法。

>>> obj.myPublicMethod()
public method

接下来,我尝试调用它的私有方法。

>>> obj.__myPrivateMethod()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: MyClass instance has no attribute '__myPrivateMethod'

这里一切看起来都很好; 我们无法调用它。 事实上,它是“私人的”。 嗯,实际上并非如此。 在对象上运行 dir() 会揭示 Python 为所有“私有”方法神奇地创建的新神奇方法。

>>> dir(obj)
['_MyClass__myPrivateMethod', '__doc__', '__module__', 'myPublicMethod']

这个新方法的名称始终是一个下划线,后跟类名,然后是方法名。

>>> obj._MyClass__myPrivateMethod()
this is private!!

封装就这么多了,是吗?

无论如何,我一直听说 Python 不支持封装,那为什么还要尝试呢? 是什么赋予了?

Python gives us the ability to create 'private' methods and variables within a class by prepending double underscores to the name, like this: __myPrivateMethod(). How, then, can one explain this

>>>> class MyClass:
...     def myPublicMethod(self):
...             print 'public method'
...     def __myPrivateMethod(self):
...             print 'this is private!!'
...
>>> obj = MyClass()

>>> obj.myPublicMethod()
public method

>>> obj.__myPrivateMethod()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: MyClass instance has no attribute '__myPrivateMethod'

>>> dir(obj)
['_MyClass__myPrivateMethod', '__doc__', '__module__', 'myPublicMethod']

>>> obj._MyClass__myPrivateMethod()
this is private!!

What's the deal?!

I'll explain this a little for those who didn't quite get that.

>>> class MyClass:
...     def myPublicMethod(self):
...             print 'public method'
...     def __myPrivateMethod(self):
...             print 'this is private!!'
...
>>> obj = MyClass()

I create a class with a public method and a private method and instantiate it.

Next, I call its public method.

>>> obj.myPublicMethod()
public method

Next, I try and call its private method.

>>> obj.__myPrivateMethod()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: MyClass instance has no attribute '__myPrivateMethod'

Everything looks good here; we're unable to call it. It is, in fact, 'private'. Well, actually it isn't. Running dir() on the object reveals a new magical method that Python creates magically for all of your 'private' methods.

>>> dir(obj)
['_MyClass__myPrivateMethod', '__doc__', '__module__', 'myPublicMethod']

This new method's name is always an underscore, followed by the class name, followed by the method name.

>>> obj._MyClass__myPrivateMethod()
this is private!!

So much for encapsulation, eh?

In any case, I'd always heard Python doesn't support encapsulation, so why even try? What gives?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

荒芜了季节2024-07-11 15:33:06

名称加扰用于确保子类不会意外覆盖其超类的私有方法和属性。 它的设计初衷并不是为了防止来自外部的故意访问。

例如:

>>> class Foo(object):
...     def __init__(self):
...         self.__baz = 42
...     def foo(self):
...         print self.__baz
...     
>>> class Bar(Foo):
...     def __init__(self):
...         super(Bar, self).__init__()
...         self.__baz = 21
...     def bar(self):
...         print self.__baz
...
>>> x = Bar()
>>> x.foo()
42
>>> x.bar()
21
>>> print x.__dict__
{'_Bar__baz': 21, '_Foo__baz': 42}

当然,如果两个不同的类具有相同的名称,它就会崩溃。

The name scrambling is used to ensure that subclasses don't accidentally override the private methods and attributes of their superclasses. It's not designed to prevent deliberate access from outside.

For example:

>>> class Foo(object):
...     def __init__(self):
...         self.__baz = 42
...     def foo(self):
...         print self.__baz
...     
>>> class Bar(Foo):
...     def __init__(self):
...         super(Bar, self).__init__()
...         self.__baz = 21
...     def bar(self):
...         print self.__baz
...
>>> x = Bar()
>>> x.foo()
42
>>> x.bar()
21
>>> print x.__dict__
{'_Bar__baz': 21, '_Foo__baz': 42}

Of course, it breaks down if two different classes have the same name.

爱情眠于流年2024-07-11 15:33:06

重要提示:

任何 __name 形式的标识符(至少两个前导下划线,最多一个尾随下划线)都会公开替换为 _classname__name,其中 classname 是当前类名,去掉了前导下划线。

因此,__name无法直接访问,但可以通过_classname__name访问。

这并不意味着您可以按原样保护您的私有数据通过更改变量名称即可轻松访问。

来源:

官方文档中的“私有变量”部分:https://docs。 python.org/3/tutorial/classes.html#tut-private

示例

class Cat:
    def __init__(self, name='unnamed'):
        self.name = name
    def __print_my_name(self):
        print(self.name)
        
        
tom = Cat()
tom.__print_my_name() #Error
tom._Cat__print_my_name() #Prints name

Important note:

Any identifier of the form __name (at least two leading underscores, at most one trailing underscore) is publicly replaced with _classname__name, where classname is the current class name with a leading underscore(s) stripped.

Therefore, __name is not accessible directly, but can be accessed as_classname__name.

This does not mean that you can protect your private data as it is easily accessible by changing the name of the variable.

Source:

"Private Variables" section in official documentation: https://docs.python.org/3/tutorial/classes.html#tut-private

Example

class Cat:
    def __init__(self, name='unnamed'):
        self.name = name
    def __print_my_name(self):
        print(self.name)
        
        
tom = Cat()
tom.__print_my_name() #Error
tom._Cat__print_my_name() #Prints name
七禾2024-07-11 15:33:06

来自深入了解 Python,3.9。 私有函数

严格来说,私有方法是
可以在课堂之外访问,只需
不容易到达。 里面什么都没有
Python 是真正私有的; 内部,
私有方法的名称和
属性被破坏和未破坏
飞快地让他们看起来
无法通过他们的名字来访问。 你
可以访问 __parse 方法
MP3FileInfo 类的名称
_MP3FileInfo__解析。 承认这很有趣,然后承诺
永远、永远不要在真正的代码中这样做。
私有方法对于 a 来说是私有的
原因,但就像许多其他事情一样
Python,他们的隐私是
最终是一个惯例问题,而不是
力。

From Dive Into Python, 3.9. Private functions:

Strictly speaking, private methods are
accessible outside their class, just
not easily accessible. Nothing in
Python is truly private; internally,
the names of private methods and
attributes are mangled and unmangled
on the fly to make them seem
inaccessible by their given names. You
can access the __parse method of the
MP3FileInfo class by the name
_MP3FileInfo__parse. Acknowledge that this is interesting, then promise to
never, ever do it in real code.
Private methods are private for a
reason, but like many other things in
Python, their privateness is
ultimately a matter of convention, not
force.

绿萝2024-07-11 15:33:06

私有函数示例

import re
import inspect

class MyClass:

    def __init__(self):
        pass

    def private_function(self):
        try:
            function_call = inspect.stack()[1][4][0].strip()

            # See if the function_call has "self." in the beginning
            matched = re.match( '^self\.', function_call)
            if not matched:
                print 'This is a private function. Go away.'
                return
        except:
            print 'This is a private function. Go away.'
            return

        # This is the real function, only accessible inside the class #
        print 'Hey, welcome in to the function.'

    def public_function(self):
        # I can call a private function from inside the class
        self.private_function()

### End ###

Example of a private function

import re
import inspect

class MyClass:

    def __init__(self):
        pass

    def private_function(self):
        try:
            function_call = inspect.stack()[1][4][0].strip()

            # See if the function_call has "self." in the beginning
            matched = re.match( '^self\.', function_call)
            if not matched:
                print 'This is a private function. Go away.'
                return
        except:
            print 'This is a private function. Go away.'
            return

        # This is the real function, only accessible inside the class #
        print 'Hey, welcome in to the function.'

    def public_function(self):
        # I can call a private function from inside the class
        self.private_function()

### End ###
忘东忘西忘不掉你2024-07-11 15:33:06

当我第一次从 Java 转向 Python 时,我讨厌这个。 吓死我了。

今天,这可能是关于 Python 我最喜欢的一件事。

我喜欢在一个平台上,人们彼此信任,并且不需要在代码周围建立坚不可摧的墙。 在强封装语言中,如果 API 有错误,并且您已经找出了问题所在,您可能仍然无法解决它,因为所需的方法是私有的。 在 Python 中,态度是:“当然”。 如果你认为你了解情况,也许你已经读过它,那么我们只能说“祝你好运!”。

请记住,封装与“安全”或让孩子远离草坪甚至没有微弱的关系。 这只是另一种应该用来使代码库更易于理解的模式。

When I first came from Java to Python I hated this. It scared me to death.

Today it might just be the one thing I love most about Python.

I love being on a platform, where people trust each other and don't feel like they need to build impenetrable walls around their code. In strongly encapsulated languages, if an API has a bug, and you have figured out what goes wrong, you may still be unable to work around it because the needed method is private. In Python the attitude is: "sure". If you think you understand the situation, perhaps you have even read it, then all we can say is "good luck!".

Remember, encapsulation is not even weakly related to "security", or keeping the kids off the lawn. It is just another pattern that should be used to make a code base easier to understand.

一枫情书2024-07-11 15:33:06

为什么 Python 的“私有”方法实际上并不是私有的?

据我了解,它们不能是私有的。 如何保护隐私?

显而易见的答案是“私有成员只能通过 self 访问”,但这是行不通的 - self 在 Python 中并不特殊。 它只不过是函数第一个参数的常用名称。

Why are Python's 'private' methods not actually private?

As I understand it, they can't be private. How could privacy be enforced?

The obvious answer is "private members can only be accessed through self", but that wouldn't work - self is not special in Python. It is nothing more than a commonly-used name for the first parameter of a function.

阳光的暖冬2024-07-11 15:33:06

这并不是说你绝对不能绕过任何语言中成员的私有性(C++ 中的指针算术和 .NET/Java 中的反射)。

关键是,如果您尝试意外调用私有方法,则会出现错误。 但如果你想搬起石头砸自己的脚,那就去做吧。

您不会尝试通过 OO 封装来保护您的东西,是吗?

It's not like you absolutely can't get around privateness of members in any language (pointer arithmetics in C++ and reflections in .NET/Java).

The point is that you get an error if you try to call the private method by accident. But if you want to shoot yourself in the foot, go ahead and do it.

You don't try to secure your stuff by OO-encapsulation, do you?

南薇2024-07-11 15:33:06

常用的短语是“我们都是同意的成年人”。 通过在前面添加一个单下划线(不公开)或双下划线(隐藏),您可以告诉类的用户您希望该成员以某种方式成为“私有”。 但是,您相信其他人都会负责任地行事并尊重这一点,除非他们有令人信服的理由不这样做(例如调试器和代码完成)。

如果您确实必须拥有私有的东西,那么您可以在扩展中实现它(例如,在 C 中用于 CPython )。 然而,在大多数情况下,您只需学习 Pythonic 的做事方式即可。

The phrase commonly used is "we're all consenting adults here". By prepending a single underscore (don't expose) or double underscore (hide), you're telling the user of your class that you intend the member to be 'private' in some way. However, you're trusting everyone else to behave responsibly and respect that, unless they have a compelling reason not to (e.g., debuggers and code completion).

If you truly must have something that is private, then you can implement it in an extension (e.g., in C for CPython). In most cases, however, you simply learn the Pythonic way of doing things.

命硬2024-07-11 15:33:06

当模块属性名称以单个下划线开头(例如_foo)时,存在类似的行为。

使用 from* 方法时,这样命名的模块属性不会被复制到导入模块中,例如:

from bar import *

但是,这是一种约定,而不是语言约束。 这些不是私有属性; 它们可以被任何进口商引用和操作。 有人认为,正因为如此,Python 无法实现真正​​的封装。

Similar behavior exists when module attribute names begin with a single underscore (e.g. _foo).

Module attributes named as such will not be copied into an importing module when using the from* method, e.g.:

from bar import *

However, this is a convention and not a language constraint. These are not private attributes; they can be referenced and manipulated by any importer. Some argue that because of this, Python can not implement true encapsulation.

热鲨2024-07-11 15:33:06

对于 Python 3.4,这是行为:

>>> class Foo:
        def __init__(self):
                pass
        def __privateMethod(self):
                return 3
        def invoke(self):
                return self.__privateMethod()


>>> help(Foo)
Help on class Foo in module __main__:

class Foo(builtins.object)
 |  Methods defined here:
 |
 |  __init__(self)
 |
 |  invoke(self)
 |
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |
 |  __dict__
 |      dictionary for instance variables (if defined)
 |
 |  __weakref__
 |      list of weak references to the object (if defined)

 >>> f = Foo()
 >>> f.invoke()
 3
 >>> f.__privateMethod()
 Traceback (most recent call last):
   File "<pyshell#47>", line 1, in <module>
     f.__privateMethod()
 AttributeError: 'Foo' object has no attribute '__privateMethod'

9.6 开始。 私有变量

请注意,修改规则主要是为了避免意外而设计的; 仍然可以访问或修改被视为私有的变量。这甚至在特殊情况下很有用,例如在调试器中。

With Python 3.4, this is the behaviour:

>>> class Foo:
        def __init__(self):
                pass
        def __privateMethod(self):
                return 3
        def invoke(self):
                return self.__privateMethod()


>>> help(Foo)
Help on class Foo in module __main__:

class Foo(builtins.object)
 |  Methods defined here:
 |
 |  __init__(self)
 |
 |  invoke(self)
 |
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |
 |  __dict__
 |      dictionary for instance variables (if defined)
 |
 |  __weakref__
 |      list of weak references to the object (if defined)

 >>> f = Foo()
 >>> f.invoke()
 3
 >>> f.__privateMethod()
 Traceback (most recent call last):
   File "<pyshell#47>", line 1, in <module>
     f.__privateMethod()
 AttributeError: 'Foo' object has no attribute '__privateMethod'

From 9.6. Private Variables:

Note that the mangling rules are designed mostly to avoid accidents; it still is possible to access or modify a variable that is considered private. This can even be useful in special circumstances, such as in the debugger.

猥琐帝2024-07-11 15:33:06

关于私有方法和属性最重要的关注点是告诉开发人员不要在类之外调用它,这就是封装。 人们可能会误解封装的安全性。 当一个人故意使用像你提到的(下面)这样的语法时,你就不需要封装。

obj._MyClass__myPrivateMethod()

我已经从 C# 迁移过来,一开始这对我来说也很奇怪,但过了一段时间我就意识到,只有 Python 代码设计者看待 OOP 的方式有所不同。

The most important concern about private methods and attributes is to tell developers not to call it outside the class and this is encapsulation. One may misunderstand security from encapsulation. When one deliberately uses syntax like that (below) you mentioned, you do not want encapsulation.

obj._MyClass__myPrivateMethod()

I have migrated from C# and at first it was weird for me too but after a while I came to the idea that only the way that Python code designers think about OOP is different.

赴月观长安2024-07-11 15:33:06

这只是这些语言设计选择之一。 在某种程度上,他们是有道理的。 他们这样做是为了让你需要不遗余力地尝试调用该方法,如果你真的那么需要它,你必须有一个很好的理由!

调试挂钩和测试作为可能的应用程序浮现在脑海中,当然要负责任地使用。

It's just one of those language design choices. On some level they are justified. They make it so you need to go pretty far out of your way to try and call the method, and if you really need it that badly, you must have a pretty good reason!

Debugging hooks and testing come to mind as possible applications, used responsibly of course.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文