756

Python では、次のように名前の前に 2 つのアンダースコアを追加することで、クラス内に「プライベート」メソッドと変数を作成できます__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 がカプセル化をサポートしていないと聞いていました。何を与える?

4

12 に答える 12

662

名前のスクランブリングは、サブクラスがスーパークラスのプライベートメソッドと属性を誤ってオーバーライドしないようにするために使用されます。外部からの意図的なアクセスを防ぐようには設計されていません。

例えば:

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

もちろん、2つの異なるクラスが同じ名前を持っている場合は故障します。

于 2008-09-16T10:06:07.943 に答える
214

プライベート関数の例

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 ###
于 2010-06-30T08:24:05.460 に答える
201

私が初めて Java から Python に移行したとき、私はこれが大嫌いでした。死ぬほど怖かった。

今日、それは私が Pythonで最も気に入っていることの 1 つかもしれません。

私は、人々がお互いを信頼し、コードの周りに侵入できない壁を構築する必要があると感じないプラットフォームにいるのが大好きです。強力にカプセル化された言語では、API にバグがあり、何が問題なのかを突き止めたとしても、必要なメソッドが非公開であるため、それを回避できない可能性があります。Python では、態度は「確かに」です。状況を理解していると思われる場合、おそらくそれを読んだことさえある場合は、「頑張ってください!」としか言えません。

カプセル化は、「セキュリティ」や、子供を芝生から遠ざけることとはほとんど関係がないことを忘れないでください。これは、コード ベースを理解しやすくするために使用する必要があるもう 1 つのパターンです。

于 2009-12-22T23:25:42.737 に答える
154

Dive Into Python 3.9から。プライベート関数:

厳密に言えば、プライベート メソッドはクラスの外からアクセスできますが、簡単にはアクセスできません。Python には真にプライベートなものはありません。内部的には、プライベート メソッドと属性の名前はその場でマングルされ、マングル解除され、与えられた名前ではアクセスできないように見えます。MP3FileInfo クラスの __parse メソッドには、_MP3FileInfo__parse という名前でアクセスできます。これが興味深いことであることを認め、実際のコードでは絶対にやらないと約束してください。プライベート メソッドには理由がありますが、Python の他の多くのものと同様に、それらのプライベート性は強制ではなく、最終的に慣習の問題です。

于 2008-09-16T09:03:45.080 に答える
103

一般的に使用されるフレーズは、「ここではすべて同意した大人です」です。1 つのアンダースコア (公開しない) または 2 つのアンダースコア (非表示) を先頭に追加することで、クラスのユーザーに、メンバーを何らかの方法で「非公開」にするつもりであることを伝えます。ただし、やむを得ない理由 (デバッガやコード補完など) がない限り、他のすべての人が責任を持って行動し、それを尊重することを信頼しています。

本当にプライベートなものが必要な場合は、それを拡張機能で実装できます (たとえば、C Pythonの C で)。ただし、ほとんどの場合、単にPythonのやり方を学ぶだけです。

于 2008-09-16T09:33:18.317 に答える
34

どの言語でもメンバーのプライベート性を完全に回避できないわけではありません (C++ のポインター演算と .NET/Java のリフレクション)。

重要なのは、誤ってプライベート メソッドを呼び出そうとするとエラーになることです。しかし、あなたが自分の足を撃ちたいのなら、先に進んでください。

OO カプセル化によって自分のものを保護しようとはしませんよね?

于 2008-09-16T09:04:57.030 に答える
12

モジュール属性名が単一の下線(例:_foo)で始まる場合にも、同様の動作が存在します。

そのように指定されたモジュール属性は、メソッドを使用するときにインポートモジュールにコピーされませんfrom*。例:

from bar import *

ただし、これは慣例であり、言語の制約ではありません。これらはプライベート属性ではありません。これらは、任意のインポーターによって参照および操作できます。このため、Pythonは真のカプセル化を実装できないと主張する人もいます。

于 2008-09-17T04:29:45.137 に答える
12

これは、言語設計の選択肢の 1 つにすぎません。あるレベルでは、それらは正当化されます。彼らはそれを作っているので、メソッドを試して呼び出すにはかなり遠くまで行く必要があります. 本当にそれが必要な場合は、かなりの理由があるに違いありません.

もちろん、責任を持って使用される可能性のあるアプリケーションとして、デバッグフックとテストが思い浮かびます。

于 2008-09-16T09:09:32.430 に答える