ホームページ  >  記事  >  バックエンド開発  >  Python におけるアンダースコアの意味と使い方は何ですか?

Python におけるアンダースコアの意味と使い方は何ですか?

WBOY
WBOY転載
2023-04-20 20:04:141509ブラウズ

1. 先頭の 1 つのアンダースコア: _var

変数名とメソッド名に関しては、1 つのアンダースコア接頭辞には従来の意味があります。これはプログラマーへの注意喚起です。つまり、Python コミュニティはその意味について合意していますが、プログラムの動作には影響しません。

アンダースコア接頭辞の意味は、単一のアンダースコアで始まる変数またはメソッドが内部使用のみであることを他のプログラマに知らせることです。この規則は PEP 8 で定義されています。

これは Python では必須ではありません。 Python には、Java のような「プライベート」変数と「パブリック」変数の明確な区別がありません。これは、誰かが「これは実際にはクラスのパブリック インターフェイスの一部であることを意図したものではありません。そのままにしておいてください。」という小さな下線の警告サインを設置するようなものです。

場合によっては、変数に最も適切な名前がすでにキーワードによって占有されていることがあります。したがって、class や def のような名前は、Python では変数名として使用できません。この場合、次のようにアンダースコアを追加することで名前の競合を解決できます。

>>> def make_object(name, class):
SyntaxError: "invalid syntax"

>>> def make_object(name, class_):
...    pass

つまり、末尾の単一のアンダースコア (サフィックス) は、Python キーワードとの名前の競合を避けるための規則です。 PEP 8 では、この規則について説明しています。

3. 先頭の二重アンダースコア __var

これまで説明したすべての命名パターンの意味は、合意された規則に基づいています。二重アンダースコアで始まる Python クラス属性 (変数やメソッドを含む) の場合、状況は少し異なります。

二重アンダースコア接頭辞により、Python インタープリターはサブクラスでの名前の競合を避けるために属性名を書き換えます。

これは名前マングリングとも呼ばれます。インタープリターは、クラスが拡張されたときに競合する可能性が低くなるように変数の名前を変更します。

#これは抽象的に聞こえるかもしれません。したがって、説明するために小さなコード例をまとめました:

class Test:
   def __init__(self):
       self.foo = 11
       self._bar = 23
       self.__baz = 23

組み込みの dir() 関数を使用して、このオブジェクトのプロパティを見てみましょう:

>>> t = Test()
>>> dir(t)
['_Test__baz', '__class__', '__delattr__', '__dict__', '__dir__',
'__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
'__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__',
'__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__setattr__', '__sizeof__', '__str__', '__subclasshook__',
'__weakref__', '_bar', 'foo']

上記は次のとおりです。オブジェクトのプロパティのリスト。このリストを見て、元の変数名

foo、_bar

__baz

を探してみましょう - いくつかの興味深い変更に気づくでしょう。

self.foo 変数は、プロパティ リストでは変更されずに foo として表示されます。 self._bar も同様に動作します。クラス上では

_bar として表示されます。前に述べたように、この場合、先頭のアンダースコアは単なる慣例です。プログラマー向けのヒントです。ただし、self.__baz の場合は、状況が少し異なります。このリストで __baz

を検索すると、この名前の変数は表示されません。

__バズに何が起こったのですか? よく見ると、このオブジェクトに _Test__baz

というプロパティがあることがわかります。これは、Python インタプリタによって行われる名前のマングリングです。これは、変数がサブクラスでオーバーライドされるのを防ぐために行われます。

Test クラスを拡張する別のクラスを作成し、コンストラクターに追加された既存のプロパティをオーバーライドしてみます。

class ExtendedTest(Test):
   def __init__(self):
       super().__init__()
       self.foo = 'overridden'
       self._bar = 'overridden'
       self.__baz = 'overridden'

さて、foo、

_bar という値が返されると思います。および __baz

は ExtendedTest クラスのこのインスタンスに表示されますか?見てみましょう:

>>> t2 = ExtendedTest()
>>> t2.foo
'overridden'
>>> t2._bar
'overridden'
>>> t2.__baz
AttributeError: "'ExtendedTest' object has no attribute '__baz'"

ちょっと待って、

t2.__ baz の値を表示しようとすると AttributeError が発生するのはなぜですか?名前変更が再びトリガーされます。このオブジェクトには __baz 属性さえないことがわかります:

>>> dir(t2)
['_ExtendedTest__baz', '_Test__baz', '__class__', '__delattr__',
'__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__',
'__getattribute__', '__gt__', '__hash__', '__init__', '__le__',
'__lt__', '__module__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__', '__weakref__', '_bar', 'foo', 'get_vars']

ご覧のとおり、偶発的な事故を防ぐために

__baz_ExtendedTest__baz になります。変更:

>>> t2._ExtendedTest__baz
'overridden'

ただし、元の

_Test__baz はまだ存在します:

>>> t2._Test__baz
42
二重アンダースコア名の変更は、プログラマにとって完全に透過的です。次の例はこれを確認します:
class ManglingTest:
   def __init__(self):
       self.__mangled = 'hello'

   def get_mangled(self):
       return self.__mangled

>>> ManglingTest().get_mangled()
'hello'
>>> ManglingTest().__mangled
AttributeError: "'ManglingTest' object has no attribute '__mangled'"

名前の装飾はメソッド名にも適用されますか?はい、それも当てはまります。名前の装飾は、クラスのコンテキストにおける 2 つのアンダースコア文字

("dunders") で始まるすべての名前に影響します。

class MangledMethod:
   def __method(self):
       return 42

   def call_it(self):
       return self.__method()

>>> MangledMethod().__method()
AttributeError: "'MangledMethod' object has no attribute '__method'"
>>> MangledMethod().call_it()
42

これは、おそらく驚くべき別の名前の使用法です。 変更された例:

_MangledGlobal__mangled = 23

class MangledGlobal:
   def test(self):
       return __mangled

>>> MangledGlobal().test()
23

この例では、

_MangledGlobal__mangled という名前のグローバル変数を宣言します。次に、MangledGlobal というクラスのコンテキストで変数にアクセスします。名前が変更されたため、クラスの test() メソッド内で _MangledGlobal__mangled

グローバル変数を

__mangled

として参照できます。

名前 __mangled は 2 つのアンダースコア文字で始まるため、Python インタープリターは名前 __mangled_MangledGlobal__mangled

に自動的に展開します。これは、名前の装飾がクラス属性に特に関連付けられていないことを示します。これは、クラス コンテキストで使用される 2 つのアンダースコア文字で始まる任意の名前に対して機能します。

吸収すべきことがたくさんあります。 正直に言うと、これらの例と説明は頭から出てきませんでした。これを思いつくまでには、ある程度の調査と処理が必要でした。私は常に Python を使用しており、長年にわたって使用していますが、このようなルールや特殊なケースが常に思い浮かぶわけではありません。

プログラマーにとって最も重要なスキルは、「パターン認識」と情報をどこで探せばよいかを知ることである場合があります。この時点で少し圧倒されていると感じても、心配しないでください。時間をかけて、この記事の例をいくつか試してください。

让这些概念完全沉浸下来,以便你能够理解名称修饰的总体思路,以及我向您展示的一些其他的行为。如果有一天你和它们不期而遇,你会知道在文档中按什么来查。

4.双前导和双末尾下划线 _var_

也许令人惊讶的是,如果一个名字同时以双下划线开始和结束,则不会应用名称修饰。 由双下划线前缀和后缀包围的变量不会被Python解释器修改:

class PrefixPostfixTest:
   def __init__(self):
       self.__bam__ = 42

>>> PrefixPostfixTest().__bam__
42

但是,Python保留了有双前导和双末尾下划线的名称,用于特殊用途。 这样的例子有,__init__对象构造函数,或__call__ — 它使得一个对象可以被调用。

这些dunder方法通常被称为神奇方法 - 但Python社区中的许多人(包括我自己)都不喜欢这种方法。

最好避免在自己的程序中使用以双下划线(“dunders”)开头和结尾的名称,以避免与将来Python语言的变化产生冲突。

5.单下划线 _

按照习惯,有时候单个独立下划线是用作一个名字,来表示某个变量是临时的或无关紧要的。

例如,在下面的循环中,我们不需要访问正在运行的索引,我们可以使用“_”来表示它只是一个临时值:

>>> for _ in range(32):
...    print('Hello, World.')

你也可以在拆分(unpacking)表达式中将单个下划线用作“不关心的”变量,以忽略特定的值。 同样,这个含义只是“依照约定”,并不会在Python解释器中触发特殊的行为。 单个下划线仅仅是一个有效的变量名称,会有这个用途而已。

在下面的代码示例中,我将汽车元组拆分为单独的变量,但我只对颜色和里程值感兴趣。 但是,为了使拆分表达式成功运行,我需要将包含在元组中的所有值分配给变量。 在这种情况下,“_”作为占位符变量可以派上用场:

>>> car = ('red', 'auto', 12, 3812.4)
>>> color, _, _, mileage = car

>>> color
'red'
>>> mileage
3812.4
>>> _
12

除了用作临时变量之外,“_”是大多数Python REPL中的一个特殊变量,它表示由解释器评估的最近一个表达式的结果。

这样就很方便了,比如你可以在一个解释器会话中访问先前计算的结果,或者,你是在动态构建多个对象并与它们交互,无需事先给这些对象分配名字:

>>> 20 + 3
23
>>> _
23
>>> print(_)
23

>>> list()
[]
>>> _.append(1)
>>> _.append(2)
>>> _.append(3)
>>> _
[1, 2, 3]

以上がPython におけるアンダースコアの意味と使い方は何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。