programing

파이썬에서 이름 망글링을 사용해야 합니까?

newstyles 2023. 7. 23. 14:06

파이썬에서 이름 망글링을 사용해야 합니까?

다른 언어로, 더 나은 코드를 생성하는 데 도움이 되는 일반적인 지침은 항상 모든 것을 가능한 한 숨기도록 하는 것입니다.변수를 비공개로 할지 보호할지 여부가 확실하지 않으면 비공개로 하는 것이 좋습니다.

Python도 마찬가지입니까?처음에는 모든 항목에 두 개의 주요 밑줄을 사용하고 필요에 따라 하나의 밑줄만 덜 숨겨야 합니까?

관례상 하나의 밑줄만 사용하는 것이라면 그 근거도 알고 싶습니다.

여기 JBernardo의 답변에 제가 남긴 댓글이 있습니다.제가 이 질문을 한 이유와 Python이 다른 언어와 다른 이유를 알고 싶은 이유를 설명합니다.

저는 여러분이 모든 것이 필요한 만큼 공개되어야만 하고 더 이상 공개되지 않아야 한다고 생각하도록 훈련시키는 언어에서 왔습니다.그 이유는 이것이 의존성을 줄이고 코드를 더 안전하게 변경할 수 있게 만들 것이라는 것입니다.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)

이것은 확실히 논란의 여지가 있는 일을 하는 방법입니다.파이썬 초보자들도 싫어하고, 심지어 오래된 파이썬 사람들도 이 기본값을 싫어하지만, 어쨌든 기본값이기 때문에 불편하더라도 따르는 것을 추천합니다.

사용자에게 "Can't touch this!"(터치할 수 없습니다!) 메시지를 보내려면 변수 앞에 하나의 밑줄을 표시하는 것이 일반적입니다.이것은 단지 관례일 뿐이지만, 사람들은 그러한 것들을 다룰 때 그것을 이해하고 이중적인 주의를 기울입니다.

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

이중 밑줄은 어떻습니까?우리는 주로 이중 밑줄 마법을 사용하여 실수로 메서드를 오버로드하거나 슈퍼 클래스의 속성과 이름 충돌을 방지합니다.연장할 수업을 여러 번 쓴다면 꽤 가치가 있을 수 있습니다.

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

편집: 왜 그럴까요?음, 일반적인 파이썬 스타일은 사물을 사적으로 만드는 것을 강조하지 않습니다 - 반대로!거기에는 많은 이유들이 있습니다 - 그들 중 대부분은 논란의 여지가 있습니다...그들 중 몇 명을 봅시다.

Python에는 속성이 있습니다.

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

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

그렇다면 왜 이 기본적인 개인별 정책을 따라야 할까요?기본적으로 속성을 공개합니다.물론 Java에서는 속성에 일부 유효성 검사를 추가하기로 결정하면 다음을 모두 변경해야 하기 때문에 문제가 있습니다.

person.age = age;

당신의 코드로 말하자면, 우리가 말할 수 있습니다.

person.setAge(age);

setAge()비정상:

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

따라서 자바(및 다른 언어)에서는 기본적으로 게터와 세터를 사용합니다. 왜냐하면 게터와 세터는 쓰기 귀찮을 수 있지만 제가 설명한 상황에 처하게 되면 많은 시간을 할애할 수 있기 때문입니다.

그러나 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왜 당신은 개인 필드와 게터와 세터를 추가합니까?

(또한 Python은 Java아니며 Getter와 Setter를 사용하는 것의 해로움에 대한기사참조하십시오.)

어쨌든 모든 것이 보입니다. 그리고 숨기려고 하면 일이 복잡해집니다.

개인 속성이 있는 언어에서도 일부 성찰/자기 성찰 라이브러리를 통해 액세스할 수 있습니다.그리고 사람들은 그것을 틀 안에서 그리고 긴급한 요구를 해결하기 위해 많이 합니다.문제는 내성 도서관이 공공 속성으로 할 수 있는 복잡한 방법일 뿐이라는 것입니다.

파이썬은 매우 역동적인 언어이기 때문에 수업에 이러한 부담을 더하는 것은 역효과를 낳습니다.

문제를 확인할 수 없습니다. 확인해야 합니다.

Pythonista의 경우 캡슐화는 클래스 내부를 볼 수 없는 것이 아니라 클래스 내부를 보지 않을 수 있는 가능성입니다.캡슐화는 사용자가 내부 세부 정보에 관계없이 사용할 수 있는 구성 요소의 속성입니다.만약 당신이 그것의 구현에 대해 신경 쓰지 않고 구성 요소를 사용할 수 있다면, 그것은 (파이썬 프로그래머의 의견으로) 캡슐화됩니다.

이제는 수업을 작성했다면 구현 세부사항을 생각하지 않고 사용할 수 있고, 어떤 이유에서인지 수업 내부를 들여다보고 싶다면 문제가 없습니다.요점은 당신의 API는 좋아야 하고, 나머지는 세부 사항입니다.

귀도가 그러더군요

글쎄요, 이것은 논란의 여지가 없습니다: 사실 그는 그렇게 말했습니다. ("열린 기모노"를 찾아보세요.)")

이것은 문화입니다.

네, 몇 가지 이유가 있지만 결정적인 이유는 없습니다.이것은 주로 파이썬 프로그래밍의 문화적 측면입니다.솔직히, 그것은 다른 방법일 수도 있습니다 - 하지만 그렇지 않습니다.또한, 당신은 쉽게 반대로 물어볼 수 있습니다: 왜 일부 언어는 기본적으로 개인 속성을 사용합니까?파이썬 연습과 동일한 주요 이유로: 파이썬은 이러한 언어들의 문화이고, 각각의 선택에는 장점과 단점이 있기 때문입니다.

이미 이 문화가 있기 때문에, 당신은 그것을 따르는 것이 좋습니다.않으면 Python 에게 Python을 이 날 입니다.__할때에서 :) 다음과 같이 입력합니다.

첫 번째 - 이름 망글링이 무엇입니까?

클래정서사때이호망출다됩을 사용할 때 됩니다.__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은 현재 다음과 같이 말합니다(요약).

의 사용에 대해 약간의 논란이 있습니다.__names.

클래스를 하위 클래스로 분류하고 하위 클래스에 사용하지 않으려는 속성이 있는 경우 두 개의 선행 밑줄과 후행 밑줄 없이 이름을 지정합니다.

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

  2. 이름망은디과같버특은정용만수있다습니들도를한깅글링▁and,▁as▁와 같은 특정한 용도를 만들 수 있습니다.__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을 처음 접하는 사람들은 때때로 클래스 정의에 정의된 이름에 수동으로 액세스할 수 없을 때 무슨 일이 일어나고 있는지 이해하는 데 어려움을 겪습니다.이것은 그것에 반대하는 강력한 이유는 아니지만, 만약 여러분이 학습하는 청중이 있다면 고려해야 할 것입니다.

밑줄 하나?

관례상 하나의 밑줄만 사용하는 것이라면 그 근거도 알고 싶습니다.

제 의도가 사용자들이 속성에서 손을 떼는 것일 때, 저는 밑줄 하나만 사용하는 경향이 있지만, 제 멘탈 모델에서 하위 클래스는 이름에 액세스할 수 있기 때문입니다(어차피 쉽게 망가진 이름을 찾을 수 있기 때문에 항상 가지고 있습니다).

만약 내가 그것을 사용하는 코드를 검토하고 있다면.__접두사, 나는 그들이 왜 name mangling을 호출하는지 묻고, 만약 그들이 하나의 밑줄로도 잘 할 수 없다면, 하위 클래스가 클래스와 클래스 속성에 대해 동일한 이름을 선택하면 그럼에도 불구하고 이름 충돌이 발생한다는 것을 명심하십시오.

저는 연습이 더 나은 코드를 만들어낸다고 말하지 않을 것입니다.가시성 수정자는 당면한 작업에서 주의를 분산시킬 뿐이며 부작용으로 인터페이스가 의도한 대로 사용되도록 강제합니다.일반적으로, 가시성을 강화하면 프로그래머가 설명서를 제대로 읽지 않은 경우 상황이 엉망이 되는 것을 방지할 수 있습니다.

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

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

  1. 데이터 보호 방법에 대한 가정은 하지 않고 그냥 작성만 하면 됩니다.여기에서는 문제에 대한 이상적인 인터페이스를 만들기 위해 글을 쓴다고 가정합니다.
  2. 일반적인 "클라이언트 코드" 인터페이스의 일부가 아닌 외부에서 사용되지 않을 수 있는 항목에는 선행 밑줄을 사용합니다.
  3. 클래스 내에서 순전히 편리하거나 실수로 노출될 경우 상당한 손상을 줄 수 있는 경우에만 이중 밑줄을 사용합니다.

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

참고로 다른 언어로 보호해야 합니다.클래스가 나중에 상속될 수도 있고 사용될 수도 있다는 것을 절대 알지 못합니다.외부 코드에서 사용할 수 없거나 사용하지 않아야 하는 변수만 보호하는 것이 좋습니다.

개인 데이터로 시작해서 필요에 따라 공개해서는 안 됩니다.대신, 당신은 당신의 객체의 인터페이스를 파악하는 것으로 시작해야 합니다.즉, 세상이 보는 것(공공의 것)을 파악하는 것으로 시작한 다음 그러한 일이 발생하기 위해 필요한 사적인 것이 무엇인지 파악해야 합니다.

다른 언어들은 한때 공공적이었던 것을 사적으로 만드는 것을 어렵게 만듭니다.즉, 변수를 비공개 또는 보호하면 많은 코드가 깨집니다.하지만 파이썬의 속성은 그렇지 않습니다.내부 데이터를 다시 정렬해도 동일한 인터페이스를 유지할 수 있습니다.

_와 _의 차이점은 python이 실제로 후자를 시행하려는 시도를 한다는 것입니다.물론, 그것은 정말 열심히 노력하는 것은 아니지만 그것은 그것을 어렵게 만듭니다.단지 다른 프로그래머들에게 의도가 무엇인지 알려주는 것만으로도 그들은 위험을 무릅쓰고 무시할 수 있습니다.하지만 그 규칙을 무시하는 것은 때때로 도움이 됩니다.예를 들어 디버깅, 임시 해킹, 사용 방법이 아닌 타사 코드 작업 등이 있습니다.

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

Java/C#을 보면 둘 다 개인/보호/공개가 있습니다.이 모든 것은 컴파일 타임 구조입니다.이들은 컴파일 시에만 적용됩니다.Java/C#에서 리플렉션을 사용할 경우 개인 메서드에 쉽게 액세스할 수 있습니다.

이제 파이썬에서 함수를 호출할 때마다, 당신은 본질적으로 반사를 사용합니다.이 코드 조각들은 파이썬에서 동일합니다.

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

"도트" 구문은 후자의 코드에 대한 통사적 설탕일 뿐입니다.대부분은 getattrr을 사용하는 것이 이미 하나의 함수 호출만으로 못생겼기 때문입니다.거기서부터 더 악화될 뿐입니다.

따라서 Python이 코드를 컴파일하지 않기 때문에 Java/C# 버전의 비공개가 있을 없습니다.Java와 C#은 런타임에 함수가 비공개인지 공개인지 확인할 수 없습니다. 해당 정보가 사라졌기 때문입니다(그리고 함수가 어디에서 호출되는지도 알 수 없습니다).

이러한 정보를 바탕으로 이중 밑줄의 이름을 망글링하는 것이 "개인적인 것"을 달성하는 데 가장 적합합니다.함수가 'self' 인스턴스에서 호출되었을 때 함수가 '__'로 시작하는 것을 알아차리면 바로 거기서 이름 망글링을 수행합니다.그냥 좀 더 통사적인 설탕일 뿐입니다.그 구문 당은 데이터 멤버 액세스에 반사만 사용하는 언어에서 '개인'과 동등한 것을 허용합니다.

고지 사항:저는 파이썬 개발사에서 이런 말을 하는 사람을 들어본 적이 없습니다."개인"이 없는 진짜 이유는 문화적인 것이지만 대부분의 스크립팅/통역 언어에는 개인이 없다는 것도 알게 될 것입니다.엄격하게 적용 가능한 개인 정보는 컴파일 시간을 제외하고는 실용적이지 않습니다.

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

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

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

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

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

선택한 답변은 속성이 개인 속성의 필요성을 제거하는 방법을 설명하는 데 유용하지만 모듈 수준의 기능이 개인 메서드의 필요성을 제거한다는 것도 추가합니다.

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

다음 코드 조각은 모든 다른 사례를 설명합니다.

  • 두 개의 선행 밑줄(__a)
  • 단일 선행 밑줄(_a)
  • 밑줄 없음(a)

    class Test:
    
    def __init__(self):
        self.__a = 'test1'
        self._a = 'test2'
        self.a = 'test3'
    
    def change_value(self,value):
        self.__a = value
        return self.__a
    

테스트 개체의 모든 유효한 특성 인쇄

testObj1 = Test()
valid_attributes = dir(testObj1)
print valid_attributes

['_Test__a', '__doc__', '__init__', '__module__', '_a', 'a', 
'change_value']

여기서 __a의 이름이 _Test__a로 변경되어 이 변수가 하위 클래스에 의해 재정의되지 않도록 할 수 있습니다.이 개념은 파이썬에서 "이름 망글링"으로 알려져 있습니다.다음과 같이 액세스할 수 있습니다.

testObj2 = Test()
print testObj2._Test__a

test1

마찬가지로 _a의 경우 변수는 해당 클래스의 내부 변수로 사용해야 함을 개발자가 알려주는 것일 뿐, python 인터프리터는 액세스해도 아무 작업도 하지 않지만 좋은 방법은 아닙니다.

testObj3 = Test()
print testObj3._a

test2

변수는 공용 클래스 변수와 같은 모든 위치에서 액세스할 수 있습니다.

testObj4 = Test()
print testObj4.a

test3

답변이 도움이 되었기를 바랍니다 :)

언뜻 보기에는 다른 언어와 동일해야 하지만("기타"에서 Java 또는 C++를 의미함) 그렇지 않습니다.

Java에서는 외부에서 액세스할 수 없는 모든 변수를 비공개로 설정했습니다.동시에 Python에서는 "프라이버시"가 없기 때문에 이것을 달성할 수 없습니다(Python 원칙 중 하나인 "우리는 모두 어른입니다").따라서 이중 밑줄은 "여러분, 이 필드를 직접 사용하지 마십시오."만 의미합니다.같은 의미는 하나의 밑줄을 가지고 있으며, 동시에 고려된 클래스에서 상속받아야 할 때 두통을 일으키지 않습니다(단, 이중 밑줄로 인해 발생할 수 있는 문제의 예).

따라서 "개인" 구성원의 경우 기본적으로 단일 밑줄을 사용하는 것이 좋습니다.

"변수를 비공개로 해야 할지 보호해야 할지 확신이 서지 않는다면 비공개로 하는 것이 좋습니다." - 네, Python에서도 마찬가지입니다.

여기에는 '컨벤션'에 대한 일부 답변이 나와 있지만, 이러한 컨벤션에 대한 링크는 제공하지 않습니다.Python의 권위 있는 가이드인 PEP 8은 다음과 같이 명시하고 있습니다.

확실하지 않은 경우 비공개를 선택합니다. 공개 속성을 비공개로 만드는 것보다 나중에 공개하는 것이 더 쉽습니다.

Python의 공용과 개인, 이름 망글링의 구별은 다른 답변에서 고려되었습니다.같은 링크에서,

Python에서는 (일반적으로 불필요한 양의 작업 없이) 어떤 속성도 실제로 비공개이기 때문에 여기서 "프라이빗"이라는 용어를 사용하지 않습니다.

#Python name mangling 예제 프로그램

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


[n for n in dir(Demo) if 'any' in n]   # GIVES OUTPUT AS ['_Demo__any_name', 
                                       #    '_Demo__any_other_name_']

언급URL : https://stackoverflow.com/questions/7456807/should-i-use-name-mangling-in-python