[python] 파이썬 이름 맹 글링

다른 언어에서는 더 나은 코드를 생성하는 데 도움이되는 일반적인 지침은 항상 모든 것을 가능한 한 숨기는 것입니다. 변수가 비공개인지 보호되어야하는지 확실하지 않은 경우 비공개로 설정하는 것이 좋습니다.

파이썬도 마찬가지입니까? 처음에는 모든 것에 두 개의 선행 밑줄을 사용하고 필요에 따라 덜 숨겨야합니까 (단 하나의 밑줄 만)?

규칙이 하나의 밑줄 만 사용하는 것이라면 그 이유도 알고 싶습니다.

여기에 JBernardo의 답변 에 대한 의견이 있습니다. 이 질문을 한 이유와 Python이 다른 언어와 다른 이유를 알고 싶은 이유를 설명합니다.

저는 모든 것이 필요한만큼만 공개되어야하고 더 이상은 안된다고 생각하도록 훈련시키는 언어에서 왔습니다. 그 이유는 이것이 종속성을 줄이고 코드를 더 안전하게 변경할 수 있도록하기 때문입니다. 역으로 일을하는 파이썬 방식 (공개에서 시작하여 숨겨진쪽으로)은 나에게 이상하다.



답변

확실하지 않은 경우 “공개”상태로 두십시오. 속성 이름을 가리기 위해 아무것도 추가하지 마십시오. 내부 가치가있는 수업이 있다면 신경 쓰지 마십시오. 작성하는 대신 :

class Stack(object):

    def __init__(self):
        self.__storage = [] # Too uptight

    def push(self, value):
        self.__storage.append(value)

기본적으로 작성하십시오.

class Stack(object):

    def __init__(self):
        self.storage = [] # No mangling

    def push(self, value):
        self.storage.append(value)

이것은 확실히 논란의 여지가있는 일을하는 방법입니다. Python 초보자는이 기본값을 싫어하고 일부 오래된 Python 사용자도이 기본값을 경멸합니다. 그러나 어쨌든 기본값이므로 불편 함을 느끼더라도 따라가는 것이 좋습니다.

당신이 경우 실제로 메시지를 보내려면 “이 접촉 할 수 없습니다!” 사용자에게 일반적인 방법은 변수 앞에 밑줄 하나를 추가하는 것 입니다. 이것은 단지 관습이지만 사람들은 그것을 이해하고 그러한 것들을 다룰 때 두 배의주의를 기울입니다.

class Stack(object):

    def __init__(self):
        self._storage = [] # This is ok but pythonistas use it to be relaxed about it

    def push(self, value):
        self._storage.append(value)

속성 이름과 속성 이름 간의 충돌을 피하는데도 유용 할 수 있습니다.

 class Person(object):
     def __init__(self, name, age):
         self.name = name
         self._age = age if age >= 0 else 0

     @property
     def age(self):
         return self._age

     @age.setter
     def age(self, age):
         if age >= 0:
             self._age = age
         else:
             self._age  = 0

이중 밑줄은 어떻습니까? 글쎄, 이중 밑줄 마법은 주로 메서드의 우발적 오버로딩과 슈퍼 클래스의 속성과 이름 충돌을 피하기 위해 사용됩니다 . 여러 번 확장 될 것으로 예상되는 클래스를 작성하는 경우 매우 유용 할 수 있습니다.

다른 용도로 사용하고 싶다면 할 수 있지만 일반적이거나 권장하지 않습니다.

편집 : 왜 그렇게? 글쎄, 일반적인 파이썬 스타일은 사물을 비공개로 만드는 것을 강조하지 않습니다. 반대로! 많은 이유가 있습니다. 대부분 논란이되고 있습니다. 몇 가지를 살펴 보겠습니다.

파이썬에는 속성이 있습니다

오늘날 대부분의 OO 언어는 반대 접근 방식을 사용합니다. 사용해서는 안되는 것은 보이지 않아야하므로 속성은 비공개 여야합니다. 이론적으로 이것은 아무도 객체 내부의 값을 무모하게 변경하지 않기 때문에 더 관리하기 쉽고 결합이 적은 클래스를 생성합니다.

그러나 그렇게 간단하지 않습니다. 예를 들어, 자바 클래스는 많은 속성이 있습니까 단지 게터 얻을 바로 세터 설정 값을. 단일 속성을 선언하려면 7 줄의 코드가 필요합니다. 파이썬 프로그래머는 불필요하게 복잡하다고 말할 것입니다. 또한 실제로는 getter 및 setter를 사용하여 값을 변경할 수 있으므로 하나의 공용 필드를 얻기 위해이 많은 코드를 작성하기 만하면됩니다.

그렇다면이 기본 정책을 따르는 이유는 무엇입니까? 기본적으로 속성을 공개하십시오. 물론 이것은 Java에서 문제가됩니다. 속성에 일부 유효성 검사를 추가하기로 결정하면 모든 항목을 변경해야하기 때문입니다.

person.age = age;

귀하의 코드에서

person.setAge(age);

setAge() 존재:

public void setAge(int age) {
    if (age >= 0) {
        this.age = age;
    } else {
        this.age = 0;
    }
}

따라서 Java (및 기타 언어)에서 기본값은 어쨌든 getter 및 setter를 사용하는 것입니다. 왜냐하면 작성하기가 귀찮을 수 있지만 제가 설명한 상황에 처해 있다면 많은 시간을 절약 할 수 있기 때문입니다.

그러나 Python에는 속성이 있으므로 Python에서 수행 할 필요가 없습니다. 이 수업이있는 경우 :

 class Person(object):
     def __init__(self, name, age):
         self.name = name
         self.age = age

그런 다음 나이를 확인하기로 결정하면 person.age = age코드 조각 을 변경할 필요가 없습니다 . 속성을 추가하기 만하면됩니다 (아래 그림 참조).

 class Person(object):
     def __init__(self, name, age):
         self.name = name
         self._age = age if age >= 0 else 0

     @property
     def age(self):
         return self._age

     @age.setter
     def age(self, age):
         if age >= 0:
             self._age = age
         else:
             self._age  = 0

할 수 있고 여전히을 사용 person.age = age한다면 왜 private 필드와 getter 및 setter를 추가합니까?

(또한 참조 파이썬은 자바하지 않습니다getter 및 setter를 사용하는 피해에 대한이 문서 .).

어쨌든 모든 것을 볼 수 있으며 숨기려고하면 작업이 복잡해집니다.

개인 속성이있는 언어에서도 일종의 리플렉션 / 인트로 스펙 션 라이브러리를 통해 액세스 할 수 있습니다. 그리고 사람들은 프레임 워크와 긴급한 요구를 해결하기 위해 많은 일을합니다. 문제는 인트로 스펙 션 라이브러리가 공용 속성으로 할 수있는 작업을 수행하기 어려운 방법이라는 것입니다.

Python은 매우 동적 인 언어이므로 클래스에 이러한 부담을 더하는 것은 비생산적입니다.

문제를 볼 수 없습니다. 볼 필요 가 있습니다.

Pythonista의 경우 캡슐화는 클래스의 내부를 볼 수없는 것이 아니라 그것을 보지 않을 가능성입니다. 내 말은, 캡슐화는 사용자가 내부 세부 사항에 대해 걱정하지 않고 사용할 수 있도록하는 구성 요소의 속성입니다. 구현에 대해 신경 쓰지 않고 컴포넌트를 사용할 수 있다면 캡슐화됩니다 (Python 프로그래머의 의견).

이제 구현 세부 사항에 대해 생각할 필요없이 사용할 수있는 방식으로 클래스를 작성했다면 어떤 이유로 클래스 내부를 살펴보고 싶은 경우에는 문제가 없습니다 . 요점은 API가 훌륭해야하고 나머지는 세부 사항이라는 것입니다.

귀도는 그렇게 말했다

글쎄, 이것은 논란의 여지가 없습니다. 그는 실제로 그렇게 말했습니다 . ( “오픈 기모노”를 찾으십시오.)

이것은 문화입니다

예, 몇 가지 이유가 있지만 중요한 이유는 없습니다. 이것은 대부분 Python 프로그래밍의 문화적 측면입니다. 솔직히 그 반대 일 수도 있지만 그렇지 않습니다. 또한 다른 방법으로 쉽게 질문 할 수 있습니다. 일부 언어는 기본적으로 개인 속성을 사용하는 이유는 무엇입니까? 파이썬 연습과 동일한 주된 이유는 이러한 언어의 문화이기 때문에 각 선택에는 장단점이 있습니다.

이 문화가 이미 있기 때문에 따라가는 것이 좋습니다. 그렇지 않으면 Python 프로그래머가 __Stack Overflow에서 질문 할 때 코드에서 를 제거하라고 말하는 짜증을 낼 것입니다. 🙂


답변

첫째-이름 맹 글링이란 무엇입니까?

이름 맹 글링은 클래스 정의에서 __any_name또는 __any_name_, 즉 개 이상의 선행 밑줄과 최대 하나의 후행 밑줄을 사용할 때 호출됩니다 .

class Demo:
    __any_name = "__any_name"
    __any_other_name_ = "__any_other_name_"

그리고 지금:

>>> [n for n in dir(Demo) if 'any' in n]
['_Demo__any_name', '_Demo__any_other_name_']
>>> Demo._Demo__any_name
'__any_name'
>>> Demo._Demo__any_other_name_
'__any_other_name_'

의심 스러우면 어떻게합니까?

표면 상 사용은 하위 클래스가 클래스가 사용하는 속성을 사용하지 못하도록하는 것입니다.

잠재적 인 가치는 동작을 재정의하려는 하위 클래스와 이름 충돌을 피하여 부모 클래스 기능이 예상대로 계속 작동하도록하는 것입니다. 그러나 Python 문서 의 예제 는 Liskov로 대체 할 수 없으며 이것이 유용하다고 생각하는 예제는 없습니다.

단점은 코드베이스를 읽고 이해하기위한인지 부하가 ​​증가한다는 것입니다. 특히 소스에서 이중 밑줄 이름이 표시되고 디버거에서 잘린 이름이 표시되는 디버깅시 그러합니다.

내 개인적인 접근 방식은 의도적으로 피하는 것입니다. 저는 매우 큰 코드베이스에서 작업합니다. 드물게 사용되는 것은 엄지 손가락처럼 튀어 나와 정당화되지 않는 것 같습니다.

당신이 그것을 볼 때 그것을 알 수 있도록 당신은 그것을 알고 있어야합니다.

PEP 8

Python 표준 라이브러리 스타일 가이드 인 PEP 8 은 현재 다음과 같이 말합니다.

.NET Framework 사용에 대해 몇 가지 논란이 있습니다 __names.

클래스가 하위 클래스로 지정되고 하위 클래스에서 사용하지 않으려는 속성이있는 경우 이중 선행 밑줄과 후행 밑줄없이 이름을 지정하는 것이 좋습니다.

  1. 단순한 클래스 이름 만 엉켜 진 이름에 사용되므로 하위 클래스가 동일한 클래스 이름과 속성 이름을 모두 선택하더라도 이름 충돌이 발생할 수 있습니다.

  2. 이름 맹 글링은 디버깅 및 __getattr__(), 덜 편리함 과 같은 특정 용도를 만들 수 있습니다 . 그러나 이름 맹 글링 알고리즘은 잘 문서화되어 있으며 수동으로 수행하기 쉽습니다.

  3. 모든 사람이 이름 맹 글링을 좋아하는 것은 아닙니다. 우발적 인 이름 충돌을 피해야하는 필요성과 고급 발신자의 잠재적 인 사용 간의 균형을 유지하십시오.

어떻게 작동합니까?

클래스 정의에서 두 개의 밑줄 (끝 이중 밑줄없이)을 추가하면 이름이 뭉개지고 클래스 이름이 뒤에 오는 밑줄이 개체 앞에 추가됩니다.

>>> class Foo(object):
...     __foobar = None
...     _foobaz = None
...     __fooquux__ = None
...
>>> [name for name in dir(Foo) if 'foo' in name]
['_Foo__foobar', '__fooquux__', '_foobaz']

클래스 정의가 구문 분석 될 때만 이름이 엉망이됩니다.

>>> Foo.__test = None
>>> Foo.__test
>>> Foo._Foo__test
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'Foo' has no attribute '_Foo__test'

또한 Python을 처음 접하는 사람들은 클래스 정의에 정의 된 이름에 수동으로 액세스 할 수 없을 때 무슨 일이 일어나는지 이해하는 데 어려움을 겪습니다. 이것은 그것에 대한 강력한 이유는 아니지만 학습 청중이 있다면 고려해야 할 사항입니다.

밑줄 하나?

규칙이 하나의 밑줄 만 사용하는 것이라면 그 이유도 알고 싶습니다.

내 의도가 사용자가 속성에서 손을 떼지 않으려는 경우에는 밑줄 하나만 사용하는 경향이 있지만, 내 멘탈 모델에서는 하위 클래스가 이름에 액세스 할 수 있기 때문입니다. 어쨌든 망가진 이름).

__접두사 를 사용하는 코드를 검토 하는 경우 이름 맹 글링을 호출하는 이유와 단일 밑줄로 잘 수행 할 수없는 경우 하위 클래스가 클래스에 대해 동일한 이름을 선택하고 클래스 속성에도 불구하고 이름 충돌이 발생합니다.


답변

나는 연습이 더 나은 코드를 생산한다고 말하지 않을 것입니다. 가시성 수정자는 당면한 작업에서주의를 분산시킬 뿐이며 부작용으로 인터페이스가 의도 한대로 사용되도록합니다. 일반적으로 가시성을 강화하면 프로그래머가 문서를 제대로 읽지 않은 경우 문제를 해결하지 못합니다.

훨씬 더 나은 솔루션은 Python이 권장하는 경로입니다. 클래스와 변수는 잘 문서화되어야하며 동작은 명확해야합니다. 소스를 사용할 수 있어야합니다. 이것은 코드를 작성하는 훨씬 더 확장 가능하고 안정적인 방법입니다.

Python에서의 내 전략은 다음과 같습니다.

  1. 데이터를 어떻게 보호해야하는지에 대한 가정을하지 말고 망할 일만 작성하십시오. 이것은 문제에 대한 이상적인 인터페이스를 작성하기 위해 작성한다고 가정합니다.
  2. 그 물건에 대한 주요 밑줄을 사용하여 아마 외부에서 사용할 수 없습니다, 그리고 정상적인 “클라이언트 코드”인터페이스의 일부가 아닌 것입니다.
  3. 클래스 내에서 순전히 편의를 위해 이중 밑줄을 사용하거나 실수로 노출 될 경우 상당한 손상을 입힐 수 있습니다.

무엇보다도 모든 것이 무엇을하는지 명확해야합니다. 다른 사람이 사용할 경우 문서화하십시오. 1 년 내에 유용하게 사용하려면 문서화하십시오.

참고로 다른 언어 로 보호 된 상태 로 진행해야 합니다. 나중에 클래스가 상속 될 수 있고 어떤 용도로 사용 될지 알 수 없습니다. 외부 코드에서 사용할 수 없거나 사용해서는 안되는 변수 만 보호하는 것이 가장 좋습니다.


답변

개인 데이터로 시작하여 필요에 따라 공개해서는 안됩니다. 오히려 객체의 인터페이스를 파악하는 것부터 시작해야합니다. 즉, 세상이 보는 것 (공공적인 것)을 파악하고 그 일이 일어나기 위해 필요한 사적인 것들이 무엇인지 파악하는 것으로 시작해야합니다.

다른 언어는 한때 공개되었던 것을 비공개로 만들기 어렵게 만듭니다. 즉, 변수를 private 또는 protected로 만들면 많은 코드를 깨뜨릴 것입니다. 그러나 파이썬의 속성에서는 그렇지 않습니다. 오히려 내부 데이터를 재배치해도 동일한 인터페이스를 유지할 수 있습니다.

_와 __의 차이점은 파이썬이 실제로 후자를 강제로 시도한다는 것입니다. 물론 열심히 노력하지는 않지만 어렵게 만듭니다. _을 사용하면 다른 프로그래머에게 의도가 무엇인지 알릴 뿐이므로 위험에 처해도 무시할 수 있습니다. 그러나 그 규칙을 무시하는 것은 때때로 도움이됩니다. 예를 들면 디버깅, 임시 해킹, 사용하는 방식으로 사용하도록 의도되지 않은 타사 코드 작업이 있습니다.


답변

이것에 대한 좋은 답변은 이미 많이 있지만 저는 다른 답변을 드릴 것입니다. 이것은 또한 이중 밑줄이 사적인 것이 아니라고 계속 말하는 사람들에 대한 부분적인 반응이기도합니다.

Java / C #을 보면 둘 다 private / protected / public이 있습니다. 이 모든 것은 컴파일 타임 구조입니다. 입니다. 컴파일시에만 적용됩니다. Java / C #에서 리플렉션을 사용하면 private 메서드에 쉽게 액세스 할 수 있습니다.

이제 Python에서 함수를 호출 할 때마다 본질적으로 리플렉션을 사용합니다. 이러한 코드 조각은 Python에서 동일합니다.

lst = []
lst.append(1)
getattr(lst, 'append')(1)

“점”구문은 후자의 코드에 대한 구문 설탕 일뿐입니다. 대부분의 경우 getattr을 사용하는 것은 이미 하나의 함수 호출만으로 추악하기 때문입니다. 거기에서 더 악화됩니다.

따라서 Python이 코드를 컴파일하지 않으므로 Java / C # 버전의 private 이있을 수 없습니다 . Java 및 C #은 해당 정보가 사라지고 함수가 호출되는 위치를 알지 못하기 때문에 런타임에 함수가 비공개인지 공개인지 확인할 수 없습니다.

이제 그 정보와 함께 이중 밑줄의 이름 변경이 “사생활”을 달성하는 데 가장 적합합니다. 이제 ‘self’인스턴스에서 함수가 호출되고 ‘__’로 시작하는 것을 발견하면 바로 거기에서 이름 변경을 수행합니다. 그것은 단지 더 많은 통사론 적 설탕입니다. 이 구문 설탕은 데이터 멤버 액세스에만 리플렉션을 사용하는 언어에서 ‘private’와 동등한 것을 허용합니다.

면책 조항 : 저는 파이썬 개발에서 이와 같은 말을하는 사람을 들어 본 적이 없습니다. “사설”이 부족한 진짜 이유는 문화적이지만 대부분의 스크립팅 / 해석 언어에는 사적이 없다는 것도 알 수 있습니다. 엄격하게 시행 가능한 private은 컴파일 시간을 제외하고는 실용적이지 않습니다.


답변

첫째 : 데이터를 숨기려는 이유는 무엇입니까? 그게 왜 그렇게 중요할까요?

대부분의 경우 당신은 정말로하고 싶지 않지만 다른 사람들이하기 때문에 그렇게합니다.

정말로 사람들이 무언가를 사용하는 것을 정말로 원하지 않는다면, 그 앞에 밑줄 하나를 추가 하십시오. 그게 다야 … Pythonistas는 하나의 밑줄이있는 것이 매번 작동하는 것이 보장되지 않으며 사용자가 모르게 변경 될 수 있음을 알고 있습니다.

그것이 우리가 사는 방식이고 우리는 괜찮습니다.

두 개의 밑줄을 사용하면 클래스가 하위 클래스에 너무 나빠져서 그런 식으로 작업하고 싶지 않을 것입니다.


답변

선택한 답변은 속성개인 속성에 대한 필요성을 제거하는 방법을 설명하는 좋은 작업을 수행 하지만 모듈 수준에서 해당 함수를 추가하여 개인 메서드 의 필요성을 제거합니다 .

모듈 수준에서 메서드를 함수로 전환하면 하위 클래스가이를 재정의 할 기회가 제거됩니다. 일부 기능을 모듈 수준으로 이동하는 것은 이름 맹 글링을 사용하여 메서드를 숨기려는 것보다 더 파이썬 적입니다.