Python 中的私有成员

发布于 2024-08-18 00:53:20 字数 48 浏览 3 评论 0原文

如何在 Python 中将方法和数据成员设为私有?或者Python不支持私有成员?

How can I make methods and data members private in Python? Or doesn't Python support private members?

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

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

发布评论

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

评论(10

旧人 2024-08-25 00:53:20

9.6。私有变量

“私有”实例变量
除从内部外无法访问
一个对象,Python中不存在。
然而,有一个约定是
大多数 Python 代码后面跟着:名称
带有下划线前缀(例如
_spam) 应被视为 API 的非公开部分(无论是
是一个函数、一个方法或者一个数据
成员)。它应该被视为
实施细节并受
更改恕不另行通知。

因为有一个有效的用例
类私有成员(即避免
名字与名字的冲突
由子类定义),有
对这种机制的支持有限,
称为名称修改。任何标识符
__spam 形式(至少两个
前导下划线,最多一个
尾随下划线)在文本上是
替换为 _classname__spam,其中
classname 是当前类名
删除前导下划线。
这种破坏是在不考虑的情况下完成的
的句法位置
标识符,只要出现
在类的定义中。

因此,例如

class Test:
    def __private_symbol(self):
        pass
    def normal_symbol(self):
        pass
 
print dir(Test)

将输出:

['_Test__private_symbol', 
'__doc__', 
'__module__', 
'normal_symbol']

__private_symbol应该被考虑一个私有方法,但仍然可以通过 _Test__private_symbol 访问它。

9.6. Private Variables

“Private” instance variables that
cannot be accessed except from inside
an object, don’t exist in Python.
However, there is a convention that is
followed by most Python code: a name
prefixed with an underscore (e.g.
_spam) should be treated as a non-public part of the API (whether it
is a function, a method or a data
member). It should be considered an
implementation detail and subject to
change without notice.

Since there is a valid use-case for
class-private members (namely to avoid
name clashes of names with names
defined by subclasses), there is
limited support for such a mechanism,
called name mangling. Any identifier
of the form __spam (at least two
leading underscores, at most one
trailing underscore) is textually
replaced with _classname__spam, where
classname is the current class name
with leading underscore(s) stripped.
This mangling is done without regard
to the syntactic position of the
identifier, as long as it occurs
within the definition of a class.

So, for example,

class Test:
    def __private_symbol(self):
        pass
    def normal_symbol(self):
        pass
 
print dir(Test)

will output:

['_Test__private_symbol', 
'__doc__', 
'__module__', 
'normal_symbol']

__private_symbol should be considered a private method, but it would still be accessible through _Test__private_symbol.

怪我闹别瞎闹 2024-08-25 00:53:20

其他答案提供了技术细节。一方面,我想强调 Python 与 C++/Java 等语言(根据您的问题,我认为您很熟悉)之间的哲学差异。

Python(以及 Perl)的普遍态度是,属性的​​“隐私”是对程序员的请求,而不是编译器/解释器的铁丝网。这个想法在此邮件中得到了很好的总结,并且经常被引用“我们都是同意的成年人”,因为它“假设”程序员有足够的责任不干涉内部。前导下划线用作礼貌消息,表明该属性是内部属性。

另一方面,如果您确实想要访问某些应用程序的内部结构(一个值得注意的例子是像 pydoc 这样的文档生成器),您可以随意这样做。作为一名程序员,你有责任知道自己在做什么并正确地完成它,而不是语言强迫你按的方式做事。

The other answers provide the technical details. I'd like to emphasise the difference in philosophy between Python on one hand and languages like C++/Java (which I presume you're familiar with based on your question).

The general attitude in Python (and Perl for that matter) is that the 'privacy' of an attribute is a request to the programmer rather than a barbed wire fence by the compiler/interpreter. The idea is summarised well in this mail and is often referred to as "We're all consenting adults" since it 'assumes' that the programmer is responsible enough to not meddle with the insides. The leading underscores serve as a polite message saying that the attribute is internal.

On the other hand, if you do want to access the internals for some applications (a notable example is documentation generators like pydoc), you're free to do so. Onus is on you as a programmer to know what you're doing and do it properly rather than on the language to force you do to things it's way.

望喜 2024-08-25 00:53:20

Python 中没有任何其他访问保护机制的 privatePython 风格指南 中记录了一个约定,用于向用户指示你的班级他们不应该访问某些属性。

  • _single_leading_underscore:弱“内部使用”指示符。例如 from M import * 不会导入名称以下划线开头的对象。

  • single_trailing_underscore_:按约定使用以避免与Python关键字冲突,例如Tkinter.Toplevel(master, class_='ClassName')

  • __double_leading_underscore:命名类属性时,调用名称修饰(在类 FooBar 中,__boo 变为 _FooBar__boo;见下文)。

There are no private of any other access protection mechanisms in Python. There is a convention documented in the Python style guide for indicating to the users of your your class that they should not be accessing certain attribute.

  • _single_leading_underscore: weak "internal use" indicator. E.g. from M import * does not import objects whose name starts with an underscore.

  • single_trailing_underscore_: used by convention to avoid conflicts with Python keyword, e.g. Tkinter.Toplevel(master, class_='ClassName')

  • __double_leading_underscore: when naming a class attribute, invokes name mangling (inside class FooBar, __boo becomes _FooBar__boo; see below).

追我者格杀勿论 2024-08-25 00:53:20

如果是 Python 函数的名称,
类方法或属性开头
(但不以)两个
下划线,它是私有的;
一切
否则是公开的。 Python没有概念
受保护类方法(可访问
只在自己的阶级和后代中
类)。类方法是
私有(只能在自己的
类)或公共(可从
任何地方)。

深入了解 Python

If the name of a Python function,
class method, or attribute starts with
(but doesn't end with) two
underscores, it's private;
everything
else is public. Python has no concept
of protected class methods (accessible
only in their own class and descendant
classes). Class methods are either
private (accessible only in their own
class) or public (accessible from
anywhere).

Dive Into Python

厌倦 2024-08-25 00:53:20

Python 不直接支持隐私。程序员需要知道何时可以安全地从外部修改属性,但无论如何使用 python,你可以通过一些小技巧实现诸如 private 之类的东西。
现在让我们看看一个人是否可以将任何内容设为私有。

class Person(object):

    def __priva(self):
        print "I am Private"
   
    def publ(self):
        print " I am public"
   
    def callpriva(self):
        self.__priva()

现在当我们执行时:

>>> p = Person()
>>> p.publ()
 I am public
>>> p.__priva()
Traceback (most recent call last):
  File "", line 1, in 
    p.__priva()
AttributeError: 'Person' object has no attribute '__priva'
​#Explanation   : You can see  here we are not able to fetch that private method directly.
>>> p.callpriva()
I am Private
#​Explanation  : Here we can access private method inside class​

那么某人如何访问该变量???
你可以这样做:

>>> p._Person__priva
I am Private

哇,实际上,如果 python 获取任何以双下划线开头的变量,则通过在开头添加一个下划线和类名来“翻译”:

注意:如果你不想要这样名称更改,但您仍然想发送信号让其他对象远离,您可以使用单个初始下划线名称,带有初始下划线的名称不会通过带星号的导入导入(来自模块导入*)
示例:

#test.py
def hello():
    print "hello"
def _hello():
    print "Hello private"

#----------------------
#test2.py
from test import *
print hello()
print _hello()

输出-->

hello
Traceback (most recent call last):
  File "", line 1, in 
NameError: name '_hello' is not defined

现在,如果我们手动调用 _hello 。

#test2.py
from test import _hello , hello
print hello()
print _hello()

输出-->

hello
hello private

最后:Python 并没有真正具有同等的隐私支持,尽管是单一的
首字母双下划线在某种程度上确实为您提供了两个级别的隐私

Python does not support privacy directly . Programmer need to know when it is safe to modify attribute from outside but anyway with python you can achieve something like private with little tricks.
Now let's see a person can put anything private to it or not.

class Person(object):

    def __priva(self):
        print "I am Private"
   
    def publ(self):
        print " I am public"
   
    def callpriva(self):
        self.__priva()

Now When we will execute :

>>> p = Person()
>>> p.publ()
 I am public
>>> p.__priva()
Traceback (most recent call last):
  File "", line 1, in 
    p.__priva()
AttributeError: 'Person' object has no attribute '__priva'
​#Explanation   : You can see  here we are not able to fetch that private method directly.
>>> p.callpriva()
I am Private
#​Explanation  : Here we can access private method inside class​

​Then how someone can access that variable ???
You can do like :

>>> p._Person__priva
I am Private

​wow , actually if python is getting any variable starting with double underscore are “translated” by adding a single underscore and the class name to the beginning:

Note : If you do not want this name changing but you still want to send a signal for other objects to stay away, you can use a single initial underscore names with an initial underscore aren’t imported with starred imports (from module import *)
Example :

#test.py
def hello():
    print "hello"
def _hello():
    print "Hello private"

#----------------------
#test2.py
from test import *
print hello()
print _hello()

output-->

hello
Traceback (most recent call last):
  File "", line 1, in 
NameError: name '_hello' is not defined

Now if we will call _hello manually .

#test2.py
from test import _hello , hello
print hello()
print _hello()

output-->

hello
hello private

Finally : Python doesn’t really have an equivalent privacy support, although single
and double initial underscores do to some extent give you two levels of privacy

ι不睡觉的鱼゛ 2024-08-25 00:53:20

这可能有效:

import sys, functools

def private(member):
    @functools.wraps(member)
    def wrapper(*function_args):
      myself = member.__name__
      caller = sys._getframe(1).f_code.co_name
      if (not caller in dir(function_args[0]) and not caller is myself):
         raise Exception("%s called by %s is private"%(myself,caller))
      return member(*function_args)
    return wrapper

class test:
   def public_method(self):
      print('public method called')

   @private
   def private_method(self):
      print('private method called')

t = test()
t.public_method()
t.private_method()

This might work:

import sys, functools

def private(member):
    @functools.wraps(member)
    def wrapper(*function_args):
      myself = member.__name__
      caller = sys._getframe(1).f_code.co_name
      if (not caller in dir(function_args[0]) and not caller is myself):
         raise Exception("%s called by %s is private"%(myself,caller))
      return member(*function_args)
    return wrapper

class test:
   def public_method(self):
      print('public method called')

   @private
   def private_method(self):
      print('private method called')

t = test()
t.public_method()
t.private_method()
固执像三岁 2024-08-25 00:53:20

这是一个有点长的答案,但我认为它触及了真正问题的根源——可见范围。当我艰难地完成这个任务时,请坚持住!

简单地导入模块并不一定能让应用程序开发人员访问其所有类或方法;如果我实际上无法查看模块源代码,我如何知道可用的内容?有人(或某件事)必须告诉我我能做什么,并解释如何使用我被允许使用的功能,否则整件事对我来说毫无用处。

那些通过导入模块基于基本类和方法开发更高级别抽象的人会收到一份规范文档——而不是实际的源代码。

模块规范描述了客户端开发人员可见的所有功能。在处理大型项目和软件项目团队时,模块的实际实现应该始终对使用它的人隐藏——它是一个带有外部世界接口的黑匣子。对于 OOD 纯粹主义者来说,我认为技术术语是“解耦”和“连贯性”。模块用户只需要知道接口方法,而不必担心实现细节。

在没有首先更改其底层规范文档的情况下,切勿更改模块,这可能需要在某些组织中进行审查/批准才能更改代码。

作为业余程序员(现已退休),我启动了一个新模块,其中规范文档实际上写成模块顶部的巨大注释块,这将是用户在规范库中实际看到的部分。由于只有我一个人,所以我还没有建立一个库,但这很容易做到。

然后我开始编码,编写各种类和方法,但没有函数体——只有像“print()”这样的空打印语句——足以让模块编译而没有语法错误。当这一步完成后,我编译完成的空模块——这是我的规范。如果我在项目团队工作,我会提出这个规范/界面以供审查和使用。在继续充实身体之前进行评论。

我一次充实每个方法的主体并进行相应的编译,确保立即修复语法错误。这也是开始在底部编写临时“主”执行部分以在编码时测试每个方法的好时机。编码/测试完成后,所有测试代码都会被注释掉,直到您再次需要它(如果需要更新)。

在现实世界的开发团队中,规范注释块也会出现在文档控制库中,但那是另一回事了。要点是:作为模块客户端,您只能看到此规范,而不是源代码。

PS:早在时间开始之前,我就在国防航空航天社区工作,我们做了一些非常酷的事情,但是诸如专有算法和敏感系统控制逻辑之类的东西都在超级安全软件库中严格存储和加密。我们可以访问模块/包接口,但不能访问黑盒实现主体。有一个文档管理工具可以处理所有系统级设计、软件规格、源代码和测试记录——所有这些都同步在一起。政府对软件质量保证标准有严格的要求。有人记得一种叫做“Ada”的语言吗?我才多大岁数啊!

This is kinda a l-o-n-g answer but I think it gets to the root of the real problem here -- scope of visibility. Just hang in there while I slog through this!

Simply importing a module need not necessarily give the application developer access to all of its classes or methods; if I can't actually SEE the module source code how will I know what's available? Some one (or some THING) has to tell me what I can do and explain how to use those features I'm allowed to use, otherwise the whole thing is useless to me.

Those developing higher-level abstractions based on fundamental classes and methods via imported modules are presented with a specification DOCUMENT -- NOT the actual source code.

The module spec describes all the features intended to be visible to the client developer. When dealing with large projects and software project teams, the actual implementation of a module should ALWAYS remain hidden from those using it -- it's a blackbox with an interface to the outside world. For OOD purists, I believe the techie terms are "decoupling" and "coherence". The module user need only know the interface methods without being burden with the details of implementation.

A module should NEVER be changed without first changing its underlying spec document, which may require review / approval in some organizations prior to changing the code.

As hobby programmer (retired now), I start a new module with the spec doc actually written out as a giant comment block at the top of the module, this will be the part the user actually sees in the spec library. Since it's just me, I've yet to set up a library, but it would be easy enough to do.

Then I begin coding by writing the various classes and methods but without functional bodies -- just null print statements like "print()" -- just enough to allow the module to compile without syntax errors. When this step is complete I compile the completed null-module -- this is my spec. If I were working on a project team, I would present this spec/interface for review & commentary before proceeding with fleshing out the body.

I flesh out the bodies of each method one at a time and compile accordingly, ensuring syntax errors are fixed immediately on-the-fly. This is also a good time to start writing a temporary "main" execution section at the bottom to test each method as you code it. When the coding/testing are complete, all of the test code is commented out until you need it again should updates become necessary.

In a real-world development team, the spec comment block would also appear in a document control library, but that's another story. The point is: you, as the module client, see only this spec and NOT the source code.

PS: long before the beginning of time, I worked in the defense aerospace community and we did some pretty cool stuff, but things like proprietary algorithms and sensitive systems control logic were tightly vaulted and encrypted in super-duper secure software libraries. We had access to module / package interfaces but NOT the blackbox implementation bodies. There was a document management tool that handled all system-level designs, software specs, source code and test records -- it was all synched together. The government had strict requirements software quality assurance standards. Anyone remember a language called "Ada"? That's how old I am!

泪之魂 2024-08-25 00:53:20

PEP 8

“方法名称和实例变量”部分 https://peps.python.org/pep-0008/#method-names-and-instance-variables

仅对非公共方法和实例变量使用一个前导下划线。

为了避免与子类发生名称冲突,请使用两个前导下划线来调用 Python 的名称修饰规则。

Python 将这些名称与类名混合在一起:如果类 Foo 有一个名为 __a 的属性,则 Foo.__a 无法访问它。 (坚持不懈的用户仍然可以通过调用 Foo._Foo__a 来获得访问权限。)通常,双前导下划线应该仅用于避免与设计为子类化的类中的属性发生名称冲突。

注意:关于 __names 的使用存在一些争议(见下文)。

“为继承而设计”部分
https://peps.python.org/pep-0008/#designing -用于继承

始终决定类的方法和实例变量(统称为“属性”)应该是公共的还是非公共的。如有疑问,请选择非公开;稍后将其公开比将公共属性设为非公开更容易。

公共属性是您期望与您的类无关的客户端使用的属性,并且您承诺避免向后不兼容的更改。非公共属性是指那些不打算被第三方使用的属性;您不保证非公共属性不会更改甚至被删除。

我们在这里不使用术语“私有”,因为在 Python 中没有属性是真正私有的(通常不需要做不必要的工作)。

另一类属性是“子类 API”的一部分(在其他语言中通常称为“受保护”)。有些类被设计为继承、扩展或修改类行为的各个方面。在设计这样的类时,请注意明确决定哪些属性是公共的,哪些是子类 API 的一部分,以及哪些确实只能由基类使用。

考虑到这一点,以下是 Pythonic 指南:

  • 公共属性不应有前导下划线。

  • 如果您的公共属性名称与保留关键字冲突,请在您的属性名称后添加一个尾随下划线。这比缩写或损坏的拼写更好。 (但是,尽管有这条规则,“cls”是任何已知为类的变量或参数的首选拼写,尤其是类方法的第一个参数。)

    注释 1:请参阅上面的类方法的参数名称建议。

  • 对于简单的公共数据属性,最好只公开属性名称,而不需要复杂的访问器/修改器方法。请记住,如果您发现简单的数据属性需要扩展功能行为,Python 提供了一条未来增强的简单途径。在这种情况下,请使用属性将功能实现隐藏在简单的数据属性访问语法后面。

    注 1:尽量保持功能行为没有副作用,尽管缓存等副作用通常是没问题的。

    注2:避免使用属性进行计算量大的操作;属性表示法使调用者相信访问(相对)便宜。

  • 如果您的类打算进行子类化,并且您有不希望子类使用的属性,请考虑使用双前导下划线命名它们,并且不使用尾随下划线。这会调用 Python 的名称修饰算法,其中类的名称被修饰为属性名称。如果子类无意中包含同名的属性,这有助于避免属性名称冲突。

    注意1:请注意,重整名称中仅使用简单的类名称,因此如果子类选择相同的类名称和属性名称,仍然可能会发生名称冲突。

    注释 2:名称修改可能会降低某些用途(例如调试和 getattr())的便利性。然而,名称修饰算法有详细记录并且易于手动执行。

    注 3:并不是每个人都喜欢名称修改。尝试在避免意外名称冲突的需要与高级调用者的潜在使用之间取得平衡。

PEP 8

Section "Method Names and Instance Variables" https://peps.python.org/pep-0008/#method-names-and-instance-variables

Use one leading underscore only for non-public methods and instance variables.

To avoid name clashes with subclasses, use two leading underscores to invoke Python’s name mangling rules.

Python mangles these names with the class name: if class Foo has an attribute named __a, it cannot be accessed by Foo.__a. (An insistent user could still gain access by calling Foo._Foo__a.) Generally, double leading underscores should be used only to avoid name conflicts with attributes in classes designed to be subclassed.

Note: there is some controversy about the use of __names (see below).

Section "Designing for Inheritance
" https://peps.python.org/pep-0008/#designing-for-inheritance

Always decide whether a class’s methods and instance variables (collectively: “attributes”) should be public or non-public. If in doubt, choose non-public; it’s easier to make it public later than to make a public attribute non-public.

Public attributes are those that you expect unrelated clients of your class to use, with your commitment to avoid backwards incompatible changes. Non-public attributes are those that are not intended to be used by third parties; you make no guarantees that non-public attributes won’t change or even be removed.

We don’t use the term “private” here, since no attribute is really private in Python (without a generally unnecessary amount of work).

Another category of attributes are those that are part of the “subclass API” (often called “protected” in other languages). Some classes are designed to be inherited from, either to extend or modify aspects of the class’s behavior. When designing such a class, take care to make explicit decisions about which attributes are public, which are part of the subclass API, and which are truly only to be used by your base class.

With this in mind, here are the Pythonic guidelines:

  • Public attributes should have no leading underscores.

  • If your public attribute name collides with a reserved keyword, append a single trailing underscore to your attribute name. This is preferable to an abbreviation or corrupted spelling. (However, notwithstanding this rule, ‘cls’ is the preferred spelling for any variable or argument which is known to be a class, especially the first argument to a class method.)

    Note 1: See the argument name recommendation above for class methods.

  • For simple public data attributes, it is best to expose just the attribute name, without complicated accessor/mutator methods. Keep in mind that Python provides an easy path to future enhancement, should you find that a simple data attribute needs to grow functional behavior. In that case, use properties to hide functional implementation behind simple data attribute access syntax.

    Note 1: Try to keep the functional behavior side-effect free, although side-effects such as caching are generally fine.

    Note 2: Avoid using properties for computationally expensive operations; the attribute notation makes the caller believe that access is (relatively) cheap.

  • If your class is intended to be subclassed, and you have attributes that you do not want subclasses to use, consider naming them with double leading underscores and no trailing underscores. This invokes Python’s name mangling algorithm, where the name of the class is mangled into the attribute name. This helps avoid attribute name collisions should subclasses inadvertently contain attributes with the same name.

    Note 1: Note that only the simple class name is used in the mangled name, so if a subclass chooses both the same class name and attribute name, you can still get name collisions.

    Note 2: Name mangling can make certain uses, such as debugging and getattr(), less convenient. However the name mangling algorithm is well documented and easy to perform manually.

    Note 3: Not everyone likes name mangling. Try to balance the need to avoid accidental name clashes with potential use by advanced callers.

揪着可爱 2024-08-25 00:53:20
import inspect


class Number:
    def __init__(self, value):
        self.my_private = value

    def set_private(self, value):
        self.my_private = value

    def __setattr__(self, my_private, value):
        f = inspect.stack()[1][3]
        if f not in ['__init__', 'set_private']:
            raise Exception("can't access private member-my_private")
        # the default behavior
        self.__dict__[my_private] = value


def main():
    n = Number(2)
    print(n.my_private)
    n.set_private(3)
    print(n.my_private)


if __name__ == '__main__': 
    main()
import inspect


class Number:
    def __init__(self, value):
        self.my_private = value

    def set_private(self, value):
        self.my_private = value

    def __setattr__(self, my_private, value):
        f = inspect.stack()[1][3]
        if f not in ['__init__', 'set_private']:
            raise Exception("can't access private member-my_private")
        # the default behavior
        self.__dict__[my_private] = value


def main():
    n = Number(2)
    print(n.my_private)
    n.set_private(3)
    print(n.my_private)


if __name__ == '__main__': 
    main()
染火枫林 2024-08-25 00:53:20

我使用 Python 2.7 和 3.5。我写了这段代码:

class MyOBject(object):
    def __init__(self):
        self.__private_field = 10


my_object = MyOBject()
print(my_object.__private_field)

运行它并得到:

AttributeError:“MyOBject”对象没有属性“__private_field”

请参阅:
https://www.tutorialsteacher.com/python/ python 中的私有和受保护访问修饰符

I use Python 2.7 and 3.5. I wrote this code:

class MyOBject(object):
    def __init__(self):
        self.__private_field = 10


my_object = MyOBject()
print(my_object.__private_field)

ran it and got:

AttributeError: 'MyOBject' object has no attribute '__private_field'

Please see:
https://www.tutorialsteacher.com/python/private-and-protected-access-modifiers-in-python

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