파이썬은 왜 '마법의 방법'을 사용합니까?


99

나는 최근에 파이썬을 가지고 놀았는데, 조금 이상하게 발견되는 한 가지는 '마법의 방법'을 광범위하게 사용하는 것입니다. 예를 들어 길이를 사용할 수 있도록하고 객체가 메소드를 구현 한 def __len__(self)다음 언제 호출되는지 당신은 씁니다 len(obj).

객체가 단순히 len(self)메서드 를 정의하지 않고 객체 의 구성원으로 직접 호출 하는 이유가 궁금합니다 obj.len(). 파이썬이 그렇게하는 데에는 타당한 이유가있을 것 같지만, 초보자로서 나는 아직 그들이 무엇인지 알아 내지 못했습니다.


4
일반적인 이유는 a) 역사적, b) 많은 유형의 객체 와 비슷 len()하거나 reversed()적용되는 것이지만 append()시퀀스에만 적용되는 것과 같은 방법입니다 .
Grant Paul

답변:


64

AFAIK, len 는이 점에서 특별하며 역사적 뿌리를 가지고 있습니다.

다음 은 FAQ 의 인용문 입니다. .

파이썬은 왜 일부 기능 (예 : list.index ())에 메서드를 사용하고 다른 기능 (예 : len (list))에 대해 사용합니까?

주된 이유는 역사입니다. 함수는 유형 그룹에 대해 일반적이고 메소드가 전혀없는 객체 (예 : 튜플)에서도 작동하도록 의도 된 작업에 사용되었습니다. 또한 Python의 기능적 기능 (map (), apply () 등)을 사용할 때 객체의 비정질 컬렉션에 쉽게 적용 할 수있는 함수를 갖는 것이 편리합니다.

실제로 len (), max (), min ()을 내장 함수로 구현하는 것은 실제로 각 유형의 메소드로 구현하는 것보다 코드가 적습니다. 개별 사례에 대해 고민 할 수 있지만 Python의 일부이며 지금 그러한 근본적인 변경을하기에는 너무 늦었습니다. 대규모 코드 손상을 방지하려면 함수를 유지해야합니다.

다른 "마법의 방법"(실제로는 특수 방법 이라고 함) Python 민속학에서 )은 많은 의미가 있으며 유사한 기능이 다른 언어에 존재합니다. 특수 구문을 사용할 때 암시 적으로 호출되는 코드에 주로 사용됩니다.

예를 들면 :

  • 오버로드 된 연산자 (C ++ 및 기타에 있음)
  • 생성자 / 소멸자
  • 속성에 액세스하기위한 후크
  • 메타 프로그래밍을위한 도구

등등...


2
Python과 The Principle of Least Astonishment 는 Python이 이런 방식으로 얻을 수있는 이점 중 일부에 대한 좋은 읽기입니다 (영어가 작업이 필요하다는 것을 인정하지만). 기본 요점 : 표준 라이브러리가 매우 재사용 가능하지만 여전히 재정의 할 수있는 수많은 코드를 구현할 수 있습니다.
jpmc26

20

Zen of Python에서 :

모호함에도 불구하고 추측하려는 유혹을 거부하십시오.
이를 수행하는 분명한 방법은 하나, 바람직하게는 하나만 있어야합니다.

사용자 지정 방법으로, 개발자는 다른 방법 이름처럼 자유롭게 선택할 것 - 이것은 이유 중 하나입니다 getLength(), length(), getlength()또는 무엇이든지 있습니다. Python은 공통 함수를 len()사용할 수 있도록 엄격한 이름 지정을 적용 합니다.

많은 유형의 객체에 공통적 인 모든 작업은 __nonzero__, __len__또는 같은 매직 메서드에 배치됩니다 __repr__. 하지만 대부분 선택 사항입니다.

연산자 오버로딩은 매직 메서드 (예 :)로도 수행 __le__되므로 다른 일반적인 작업에도 사용하는 것이 좋습니다.


이것은 설득력있는 주장입니다. "귀도는 OO를 정말로 믿지 않았다"는 것이 더 만족 스럽습니다 .... (다른 곳에서 주장한 것처럼).
Andy Hayden

15

파이썬은 "마법의 방법" 이라는 단어를 사용합니다. 그 방법은 실제로 당신의 프로그램을 위해 마법을 수행하기 때문입니다. Python의 매직 메서드를 사용하는 가장 큰 장점 중 하나는 객체가 내장 유형처럼 동작하도록 만드는 간단한 방법을 제공한다는 것입니다. 즉, 기본 연산자를 수행하는 추악하고 직관에 반하는 비표준 방식을 피할 수 있습니다.

다음 예를 고려하십시오.

dict1 = {1 : "ABC"}
dict2 = {2 : "EFG"}

dict1 + dict2
Traceback (most recent call last):
  File "python", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'dict' and 'dict'

사전 유형이 추가를 지원하지 않기 때문에 오류가 발생합니다. 이제 사전 클래스를 확장하고 "__add__" 매직 메서드를 추가해 보겠습니다 .

class AddableDict(dict):

    def __add__(self, otherObj):
        self.update(otherObj)
        return AddableDict(self)


dict1 = AddableDict({1 : "ABC"})
dict2 = AddableDict({2 : "EFG"})

print (dict1 + dict2)

이제 다음과 같은 출력을 제공합니다.

{1: 'ABC', 2: 'EFG'}

따라서이 방법을 추가하면 갑자기 마술이 일어나고 이전에 받았던 오류가 사라졌습니다.

나는 그것이 당신에게 일을 분명히하기를 바랍니다. 자세한 내용은 다음을 참조하십시오.

파이썬의 마법 방법에 대한 가이드 (Rafe Kettler, 2012)


9

이러한 함수 중 일부는 단일 메서드가 구현할 수있는 것 이상을 수행합니다 (슈퍼 클래스의 추상 메서드없이). 예를 들어 다음 bool()과 같이 작동합니다.

def bool(obj):
    if hasattr(obj, '__nonzero__'):
        return bool(obj.__nonzero__())
    elif hasattr(obj, '__len__'):
        if obj.__len__():
            return True
        else:
            return False
    return True

또한 100 % 확신 할 수 있습니다. bool()항상 True 또는 False를 반환 . 만약 당신이 방법에 의존한다면 당신은 당신이 무엇을 되 찾을 지 완전히 확신 할 수 없습니다.

상대적으로 복잡한 구현 (기본 매직 메서드보다 더 복잡 할 가능성이있는 것보다 더 복잡함)이있는 다른 함수는 iter()cmp(), 모든 속성 메서드 ( getattr, setattrdelattr)입니다. int강압을 수행 할 때 매직 메서드에 액세스하는 것과 같은 것 (를 구현할 수 있음 __int__)도 있지만 유형으로서의 이중 의무를 수행합니다. len(obj)실제로는 .NET과 전혀 다르지 않다고 생각하는 경우입니다 obj.__len__().


2
대신에 /를 hasattr()사용 하고 대신에 그냥 말할 것이지만 그것들은 문체 일뿐입니다. try:except AttributeError:if obj.__len__(): return True else: return Falsereturn obj.__len__() > 0
Chris Lutz

파이썬 2.6 (btw가을 bool(x)참조 x.__nonzero__())에서는 방법이 작동하지 않습니다. bool 인스턴스에는 메서드 __nonzero__()가 있으며 obj가 bool이면 코드가 계속 자신을 호출합니다. 아마도 bool(obj.__bool__())당신이 치료 한 것과 같은 방식으로 치료를 받아야 __len__합니까? (아니면이 코드가 실제로 Python 3에서 작동합니까?)
Ponkadoodle

bool ()의 순환 적 특성은 정의의 특이한 순환 특성을 반영하기 위해 의도적으로 다소 터무니 없습니다. 그것은 단순히 원시적 인 것으로 간주되어야한다는 주장이 있습니다.
Ian Bicking 2010

유일한 차이점은 (현재) 사이 len(x)x.__len__()전 길이에 대한 초과 OverflowError 올릴이다 sys.maxsize일반적 유형 파이썬 구현되지 후자있다. 그것은 기능 이라기보다는 버그에 가깝습니다. (예를 들어, 파이썬 3.2의 범위 객체는 대부분 임의로 큰 범위를 처리 할 수 ​​있지만, len그것들과 함께 사용 __len__하면 실패 할 수 있습니다. 하지만 그것들은 파이썬이 아닌 C로 구현 되었기 때문에 실패합니다)
ncoghlan

4

그들은 실제로 "마법의 이름"이 아닙니다. 주어진 서비스를 제공하기 위해 객체가 구현해야하는 인터페이스 일뿐입니다. 이러한 의미에서 다시 구현해야하는 사전 정의 된 인터페이스 정의보다 마법이 아닙니다.


1

그 이유는 대부분 역사적이지만 파이썬 len에는 적절한 메서드 대신 함수를 사용하는 몇 가지 특이점 이 있습니다.

파이썬에서 일부 작업은 예를 들어 방법으로 구현 list.index하고 dict.append다른 사람이 예를 들어, callables과 마법 방법으로 구현하는 동안, striterreversed. 두 그룹은 충분히 다르므로 다른 접근 방식이 정당합니다.

  1. 그들은 일반적입니다.
  2. str, int친구는 유형입니다. 생성자를 호출하는 것이 더 합리적입니다.
  3. 구현은 함수 호출과 다릅니다. 예를 들어 는 사용할 수없는 경우 iter호출 __getitem____iter__수 있으며 메서드 호출에 맞지 않는 추가 인수를 지원합니다. 같은 이유로 최신 버전의 Python에서 it.next()로 변경되었습니다 next(it). 더 합리적입니다.
  4. 이들 중 일부는 운영자의 가까운 친척입니다. 호출 구문있다 __iter__과는 __next__- 그것은이라고 for루프. 일관성을 위해 함수가 더 좋습니다. 그리고 특정 최적화에 더 적합합니다.
  5. 일부 기능은 단순히 어떤 방법으로 나머지 너무 비슷합니다 - repr같은 역할을 str한다. str(x)대 갖는 x.repr()것은 혼란 스러울 것입니다.
  6. 그들 중 일부는 예를 들어 실제 구현 방법을 거의 사용하지 않습니다 isinstance.
  7. 그들 중 일부는 실제 운영하며, getattr(x, 'a')일을하는 또 다른 방법입니다 x.agetattr상기 자질의 많은 주.

저는 개인적으로 첫 번째 그룹을 방법과 같고 두 번째 그룹을 운영자와 같이 부릅니다. 그다지 좋은 구별은 아니지만 어떻게 든 도움이되기를 바랍니다.

이 말 len은 두 번째 그룹에 정확히 맞지 않습니다. 거의 모든 것보다 훨씬 일반적이라는 유일한 차이점을 제외하면 첫 번째 작업에 더 가깝습니다. 그러나 그것이하는 유일한 일은를 호출하는 것입니다 __len__. 그리고 그것은 L.index. 그러나 몇 가지 차이점이 있습니다. 예를 들어, __len__와 같은 다른 기능의 구현을 위해 호출 될 수 있습니다 bool. 메소드가 호출 되면 완전히 다른 작업을 수행 하는 사용자 정의 메소드로 len중단 될 수 있습니다 .bool(x)len

요컨대, 클래스가 구현할 수있는 매우 일반적인 기능 집합이 있으며, 연산자를 통해 액세스 할 수 있고, 특수 함수 (일반적으로 연산자가 수행하는 것보다 더 많은 작업을 수행함), 객체 생성 중에 액세스 할 수 있습니다. 몇 가지 공통된 특성을 공유하십시오. 나머지는 모두 방법입니다. 그리고 len그 규칙에 대한 약간의 예외입니다.


0

위의 두 게시물에 추가 할 내용이 많지 않지만 모든 "마법"기능은 실제로 마법이 아닙니다. 인터프리터가 시작될 때 암시 적으로 / 자동으로 가져 오는 __ builtins__ 모듈의 일부입니다. 즉 :

from __builtins__ import *

프로그램이 시작되기 전에 매번 발생합니다.

나는 항상 파이썬이 대화 형 쉘에 대해서만이 작업을 수행하고 필요한 빌트인에서 다양한 부분을 가져 오기 위해 스크립트를 필요로하는 것이 더 정확할 것이라고 생각했습니다. 또한 아마도 다른 __ main__ 처리는 쉘 대 대화 형에서 좋을 것입니다. 어쨌든 모든 기능을 확인하고 기능이 없으면 어떤 것인지 확인하십시오.

dir (__builtins__)
...
del __builtins__
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.