변수 및 메서드 이름과 관련하여 단일 밑줄 접두사는 일반적인 의미를 갖습니다. 이는 프로그래머에게 상기시켜주는 것입니다. 즉, Python 커뮤니티는 이것이 의미하는 바에 동의하지만 프로그램의 동작은 영향을 받지 않는다는 의미입니다.
밑줄 접두사의 의미는 단일 밑줄로 시작하는 변수나 메소드는 내부 전용임을 다른 프로그래머에게 알리는 것입니다. 이 규칙은 PEP 8에 정의되어 있습니다.
이것은 Python에서 필수 사항이 아닙니다. Python에는 Java처럼 "개인" 변수와 "공용" 변수를 크게 구분하지 않습니다. 이는 마치 누군가가 다음과 같이 작은 밑줄 경고 표시를 붙인 것과 같습니다.
"이것은 실제로 클래스의 공개 인터페이스의 일부가 아닙니다. 그냥 그대로 두세요."
때로는 변수에 가장 적합한 이름이 이미 키워드에 의해 사용되는 경우도 있습니다. 따라서 Python에서는 class 또는 def와 같은 이름을 변수 이름으로 사용할 수 없습니다. 이 경우 밑줄을 추가하여 이름 지정 충돌을 해결할 수 있습니다:
>>> def make_object(name, class): SyntaxError: "invalid syntax" >>> def make_object(name, class_): ... pass
간단히 말해서, 단일 후행 밑줄(접미사)은 Python 키워드와의 이름 지정 충돌을 피하기 위한 규칙입니다. PEP 8은 이 규칙을 설명합니다.
지금까지 다룬 모든 명명 패턴의 의미는 합의된 규칙에서 비롯됩니다. 이중 밑줄로 시작하는 Python 클래스 속성(변수 및 메서드 포함)의 경우 상황이 약간 다릅니다.
이중 밑줄 접두어를 사용하면 Python 인터프리터가 속성 이름을 다시 작성하여 하위 클래스의 이름 충돌을 방지합니다.
이것을 이름 맹글링(name mangling)이라고도 합니다. 통역사는 클래스가 확장될 때 충돌할 가능성을 줄이기 위해 변수의 이름을 변경합니다.
이 말이 추상적으로 들리는 걸 압니다. 그래서 설명을 위해 작은 코드 예제를 만들었습니다.
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
를 찾아보겠습니다. 몇 가지 흥미로운 변경 사항을 발견할 수 있을 것입니다. foo,_bar
和__baz
- 我保证你会注意到一些有趣的变化。
self.foo变量在属性列表中显示为未修改为foo。self._bar的行为方式相同 - 它以_bar
的形式显示在类上。 就像我之前说过的,在这种情况下,前导下划线仅仅是一个约定。 给程序员一个提示而已。然而,对于self.__baz
而言,情况看起来有点不同。 当你在该列表中搜索__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'"
名称修饰是否也适用于方法名称? 是的,也适用。名称修饰会影响在一个类的上下文中,以两个下划线字符("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()方法内,以__mangled
来引用_MangledGlobal__mangled
全局变量。
Python解释器自动将名称__mangled
扩展为_MangledGlobal__mangled
_bar
로 표시됩니다. 앞서 말했듯이 이 경우 선행 밑줄은 단지 관례일 뿐입니다. 프로그래머를 위한 힌트입니다. 그러나 self.__baz
의 경우 상황이 약간 달라 보입니다. 이 목록에서 __baz
를 검색하면 이 이름을 가진 변수가 표시되지 않습니다.
__baz에게 무슨 일이 일어났나요? 자세히 살펴보면 이 개체에 _Test__baz
라는 속성이 있는 것을 볼 수 있습니다. 이는 Python 인터프리터가 수행하는 이름 맹글링입니다. 이는 하위 클래스에서 변수가 재정의되는 것을 방지하기 위해 수행됩니다. Test 클래스를 확장하는 또 다른 클래스를 만들고 생성자에 추가된 기존 속성을 재정의해 보겠습니다. 🎜class PrefixPostfixTest: def __init__(self): self.__bam__ = 42 >>> PrefixPostfixTest().__bam__ 42🎜이제 foo,
_bar
및 __baz
를 생각해보세요. 코드 값>이 ExtendedTest 클래스의 인스턴스에 표시됩니까? 살펴보겠습니다: 🎜>>> for _ in range(32): ... print('Hello, World.')🎜잠깐만,
t2.__baz
의 값을 보려고 할 때 왜 AttributeError가 발생합니까? 이름 수정이 다시 시작되었습니다! 이 객체에는 __baz
속성도 없는 것으로 나타났습니다. 🎜>>> car = ('red', 'auto', 12, 3812.4) >>> color, _, _, mileage = car >>> color 'red' >>> mileage 3812.4 >>> _ 12🎜 보시다시피
__baz
는 실수로 인한 수정을 방지하기 위해 _ExtendedTest__baz
가 됩니다. : 🎜>>> 20 + 3 23 >>> _ 23 >>> print(_) 23 >>> list() [] >>> _.append(1) >>> _.append(2) >>> _.append(3) >>> _ [1, 2, 3]🎜하지만 원본
_Test__baz
는 여전히 존재합니다. 🎜rrreee🎜이중 밑줄 이름 장식은 프로그래머에게 완전히 투명합니다. 다음 예는 이를 확인합니다. 🎜rrreee🎜이름 장식이 메소드 이름에도 적용됩니까? 예, 그것도 적용됩니다. 이름 장식은 클래스 컨텍스트에서 두 개의 밑줄 문자 ("dunders")
로 시작하는 모든 이름에 영향을 미칩니다. 🎜rrreee🎜 이것은 이름 장식 사용에 대한 또 다른 아마도 놀라운 예입니다. 🎜rrreee 🎜이 예에서는 _MangledGlobal__mangled
라는 전역 변수를 선언했습니다. 그런 다음 MangledGlobal이라는 클래스의 컨텍스트에서 변수에 액세스합니다. 맹글링이라는 이름 때문에 클래스의 test() 메서드 내에서 _MangledGlobal__mangled
전역 변수를 __mangled
로 참조할 수 있습니다. 🎜🎜Python 인터프리터는 두 개의 밑줄 문자로 시작하므로 __mangled
라는 이름을 _MangledGlobal__mangled
로 자동 확장합니다. 이는 이름 장식이 클래스 속성과 특별히 연관되어 있지 않음을 나타냅니다. 클래스 컨텍스트에서 사용되는 두 개의 밑줄 문자로 시작하는 모든 이름에 작동합니다. 🎜🎜흡수할 것이 많습니다. 🎜🎜솔직히 이런 예시와 설명이 그냥 머리에서 튀어나온 게 아니었습니다. 그것을 생각해 내기 위해서는 약간의 연구와 처리가 필요했습니다. 나는 항상 Python을 사용해왔고 수년 동안 사용해왔지만 이와 같은 규칙과 특별한 경우가 항상 마음에 떠오르는 것은 아닙니다. 🎜🎜때때로 프로그래머에게 가장 중요한 기술은 "패턴 인식"과 정보를 찾을 위치를 아는 것입니다. 이 시점에서 약간의 압도감을 느끼더라도 걱정하지 마십시오. 시간을 내어 이 기사의 몇 가지 예를 시도해 보십시오. 🎜让这些概念完全沉浸下来,以便你能够理解名称修饰的总体思路,以及我向您展示的一些其他的行为。如果有一天你和它们不期而遇,你会知道在文档中按什么来查。
_var_
也许令人惊讶的是,如果一个名字同时以双下划线开始和结束,则不会应用名称修饰。 由双下划线前缀和后缀包围的变量不会被Python解释器修改:
class PrefixPostfixTest: def __init__(self): self.__bam__ = 42 >>> PrefixPostfixTest().__bam__ 42
但是,Python保留了有双前导和双末尾下划线的名称,用于特殊用途。 这样的例子有,__init_
_对象构造函数,或__call__
— 它使得一个对象可以被调用。
这些dunder方法通常被称为神奇方法 - 但Python社区中的许多人(包括我自己)都不喜欢这种方法。
最好避免在自己的程序中使用以双下划线(“dunders”)开头和结尾的名称,以避免与将来Python语言的变化产生冲突。
按照习惯,有时候单个独立下划线是用作一个名字,来表示某个变量是临时的或无关紧要的。
例如,在下面的循环中,我们不需要访问正在运行的索引,我们可以使用“_”来表示它只是一个临时值:
>>> 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!