[python] 왜 슈퍼 클래스 __init__ 메소드가 자동으로 호출되지 않습니까?

파이썬 디자이너들은 왜 다른 언어에서와 같이 서브 클래스의 __init__()메소드가 자동으로 __init__()슈퍼 클래스 의 메소드를 호출하지 않기로 결정 했습니까? 파이썬과 추천 관용구는 실제로 다음과 같은가?

class Superclass(object):
    def __init__(self):
        print 'Do something'

class Subclass(Superclass):
    def __init__(self):
        super(Subclass, self).__init__()
        print 'Do something else'



답변

파이썬의 사이의 중요한 차이 __init__와 그 다른 언어 생성자__init__입니다 하지 그것이이다 : 생성자 초기화 (실제 생성자를 참조 나중에 ;-)입니다 (있는 경우 만 __new__) 및 작동 완전히 다르게 다시. 반면 건설 의 모든 슈퍼 클래스를 (당신이 아래로 건설 계속 “전에”의심의 여지가 이렇게) 분명히 당신이있어 말의 일부 구성하는 서브 클래스의 인스턴스를, 그 명확의 경우에는 해당되지 않습니다 초기화수퍼 클래스의 초기화를 건너 뛰고, 변경하고, 제어해야하는 사용 사례가 많기 때문에, 서브 클래스 초기화의 “중간”등에서 발생합니다.

기본적으로 이니셜 라이저의 슈퍼 클래스 위임은 파이썬에서 자동으로 동일하지 않기 때문에 이와 같은 위임은 다른 방법 에서도 자동으로 수행되지 않습니다. 이러한 “다른 언어”는 자동 슈퍼 클래스 위임을 수행하지 않습니다. 중 … 다른 방법 단지 내가 언급 한 바와 같이, (해당, 소멸자 및 경우), 생성자에 대한은 하지 파이썬의 무엇 __init__이다. (의 행동은 __new__실제로 당신의 질문과 직접적으로 관련이 없지만, 매우 독특합니다. 왜냐하면 __new__실제로 무언가를 만들 필요가없는 독특한 생성자이므로 기존 인스턴스 또는 비 인스턴스를 완벽하게 반환 할 수 있습니다. … 분명히 파이썬은 당신에게 많은 것을 제공합니다당신이 생각하는 “다른 언어들”보다 더 많은 역학을 제어 할 수 있으며, 여기 에는 자동 위임이없는 것도 포함 __new__됩니다!-)


답변

사람들이 “파이썬 (Zen of Python)”을 앵무새로 삼을 때 다소 당황 스럽습니다. 디자인 철학이다. 특정 디자인 결정은 항상 보다 구체적인 용어로 설명 될 수 있으며, 그렇지 않으면 “파이썬 (Zen of Python)”이 무엇인가를위한 변명이되어야합니다.

이유는 간단합니다. 기본 클래스를 구성하는 방법과 전혀 유사한 방식으로 파생 클래스를 구성 할 필요는 없습니다. 더 많은 매개 변수를 가질 수도 있고 더 적은 매개 변수를 가질 수도 있습니다.

class myFile(object):
    def __init__(self, filename, mode):
        self.f = open(filename, mode)
class readFile(myFile):
    def __init__(self, filename):
        super(readFile, self).__init__(filename, "r")
class tempFile(myFile):
    def __init__(self, mode):
        super(tempFile, self).__init__("/tmp/file", mode)
class wordsFile(myFile):
    def __init__(self, language):
        super(wordsFile, self).__init__("/usr/share/dict/%s" % language, "r")

이것은뿐만 아니라 모든 파생 된 방법에 적용됩니다 __init__.


답변

Java 및 C ++ 에서는 메모리 레이아웃으로 인해 기본 클래스 생성자가 호출되어야합니다.

당신은 클래스가있는 경우 BaseClass멤버를 field1, 당신은 새로운 클래스 생성 SubClass멤버 추가 field2의 다음 인스턴스 SubClass을위한 공간을 포함 field1하고를 field2. 상속하는 모든 클래스가 자체 생성자에서 초기화 를 반복하도록 요구하지 않는 한 BaseClass을 채우려면 생성자가 field1필요합니다 BaseClass. 그리고 field1비공개 인 경우 상속 클래스 초기화 할 수 없습니다field1 .

파이썬은 자바 나 C ++이 아닙니다. 모든 사용자 정의 클래스의 모든 인스턴스는 동일한 ‘모양’을 갖습니다. 기본적으로 속성을 삽입 할 수있는 사전입니다. 초기화가 완료되기 전에 모든 사용자 정의 클래스의 모든 인스턴스는 거의 동일합니다 . 아직 저장하지 않은 속성을 저장하는 장소 일뿐입니다.

따라서 파이썬 서브 클래스는 기본 클래스 생성자를 호출하지 않는 것이 좋습니다. 원하는 경우 속성 자체를 추가 할 수 있습니다. 계층 구조의 각 클래스에 대해 지정된 수의 필드에 예약 된 공간이 없으며 BaseClass메서드 에서 코드로 추가 된 특성과 메서드 에서 코드로 추가 된 특성간에 차이가 없습니다 SubClass.

일반적으로 SubClass실제로 BaseClass자신의 사용자 정의를 수행하기 전에 모든 고정 변수를 설정하려는 경우 전화를 걸 수 있습니다 BaseClass.__init__()(또는 사용 super하지만 복잡하고 때로는 자체 문제가 있습니다). 그러나 당신은 할 필요가 없습니다. 그리고 당신은 그것을 전후에, 또는 다른 주장으로 할 수 있습니다. 당신이 원한다면 지옥 BaseClass.__init__보다 다른 방법으로 from을 호출 할 수 있습니다 __init__. 어쩌면 당신은 기괴한 게으른 초기화 일이있을 수 있습니다.

파이썬은 일을 단순하게 유지함으로써 이러한 유연성을 달성합니다. __init__속성을 설정 하는 메소드를 작성하여 객체를 초기화합니다 self. 그게 다야. 정확히 메서드이기 때문에 메서드와 똑같이 동작합니다. 먼저해야 할 일이나 다른 일을하지 않으면 자동으로 일어날 일에 관한 이상하고 직관적이지 않은 규칙은 없습니다. 그것이 제공 해야하는 유일한 목적은 객체 초기화 중에 초기 속성 값을 설정하기 위해 실행하는 후크입니다. 다른 것을 원한다면 코드에 명시 적으로 작성하십시오.


답변

“명시적인 것이 묵시적인 것보다 낫다.” ‘self’를 명시 적으로 작성해야 함을 나타내는 동일한 이유입니다.

결국에는 이점이 있다고 생각합니다. 수퍼 클래스 생성자를 호출하는 것과 관련된 Java의 모든 규칙을 기억할 수 있습니까?


답변

서브 클래스는 종종 수퍼 클래스로 전달 될 수없는 추가 매개 변수를 가지고 있습니다.


답변

현재 다중 상속의 경우 메소드 해결 순서를 설명하는 다소 긴 페이지가 있습니다. http://www.python.org/download/releases/2.3/mro/

생성자가 자동으로 호출 된 경우 발생 순서를 설명하는 최소한 동일한 길이의 다른 페이지가 필요합니다. 그것은 지옥 일 것이다 …


답변

혼동을 피하기 위해 __init__()child_class에 __init__()클래스 가없는 경우 base_class 메소드를 호출 할 수 있음을 아는 것이 유용합니다 .

예:

class parent:
  def __init__(self, a=1, b=0):
    self.a = a
    self.b = b

class child(parent):
  def me(self):
    pass

p = child(5, 4)
q = child(7)
z= child()

print p.a # prints 5
print q.b # prints 0
print z.a # prints 1

실제로 파이썬의 MRO __init__()는 자식 클래스에서 찾을 수 없을 때 부모 클래스에서 찾습니다. __init__()자식 클래스에 이미 메서드가있는 경우 부모 클래스 생성자를 직접 호출해야합니다 .

예를 들어 다음 코드는 오류를 반환합니다. class parent : def init (self, a = 1, b = 0) : self.a = a self.b = b

    class child(parent):
      def __init__(self):
        pass
      def me(self):
        pass

    p = child(5, 4) # Error: constructor gets one argument 3 is provided.
    q = child(7)  # Error: constructor gets one argument 2 is provided.

    z= child()
    print z.a # Error: No attribute named as a can be found.