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


155

파이썬 디자이너들은 왜 다른 언어에서와 같이 서브 클래스의 __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'

2
__init__메소드 를 상속하기 위해 데코레이터를 작성하고 서브 클래스를 자동으로 검색하여 장식 할 수도 있습니다.
osa

2
@osa, 정말 좋은 생각입니다. 데코레이터 부분에 대해 좀 더 설명 하시겠습니까?
Diansheng

1
@osa 네 더 설명!
Charlie Parker

답변:


163

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

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


7
저에게있어 실질적인 이점은 서브 클래스 초기화의 어느 시점에서나 수퍼 클래스의 _ init () _ 를 호출 할 수 있다는 것 입니다.
kindall

53
" __init__에 대한 -1 은 생성자가 아닙니다 ... 실제 생성자는 ...입니다 __new__. 스스로 언급했듯이 __new__다른 언어의 생성자처럼 작동하지 않습니다. __init__실제로는 매우 유사합니다 (새 개체를 만드는 동안 호출되며, 개체가 할당 된 새 개체에 멤버 변수를 설정하기 위해 호출 됨). 생성자 그냥 생성자라고 부르십시오!
Ben

8
또한 " 모든 수퍼 클래스를 구성 하는 것은 분명히 서브 클래스의 인스턴스를 구성 하는 것의 일부입니다 . 이는 분명히 초기화 의 경우가 아닙니다 "라고 생각합니다. 건설 / 초기화라는 단어에는 아무도 이것을 "명백하게"만드는 것이 없습니다. 그리고 __new__슈퍼 클래스도 자동으로 호출하지 않습니다 __new__. 따라서 중요한 차이점은 생성 에는 반드시 수퍼 클래스 생성이 필요하지만 초기화__new__생성자 인 클레임과 일치하지 않는 것입니다 .
Ben

36
사실, docs.python.org/reference/datamodel.html#basic-customization 의 파이썬 문서 __init__에서 : " 생성자 에 대한 특별한 제약으로 값이 반환되지 않을 수 있습니다; 그렇게하면 런타임에 TypeError가 발생합니다 "(강조 광산). 공식적인 것은 생성자입니다. __init__
Ben

3
파이썬 / 자바 용어에서 __init__생성자라고합니다. 이 생성자는 객체가 완전히 구성되고 최종 런타임 유형을 포함한 기본 상태로 초기화 된 후에 호출되는 초기화 함수입니다. 정의되지 않은 상태의 정적으로 유형이 할당 된 객체에서 호출되는 C ++ 생성자와 동일하지 않습니다. 이것 들과는 다르기 __new__때문에, 우리는 적어도 4 가지 종류의 할당 / 구성 / 초기화 기능을 가지고 있습니다. 언어는 혼합 용어를 사용하며 중요한 부분은 용어가 아니라 동작입니다.
Elazar

36

사람들이 "파이썬 (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__.


6
이 예는 특별한 것을 제공합니까? 정적 언어도이 작업을 수행 할 수 있습니다
jean

18

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. 그게 다야. 정확히 메서드이기 때문에 메서드와 똑같이 동작합니다. 먼저해야 할 일이나 다른 일을하지 않으면 자동으로 일어날 일에 관한 이상하고 직관적이지 않은 규칙은 없습니다. 그것이 제공 해야하는 유일한 목적은 객체 초기화 중에 초기 속성 값을 설정하기 위해 실행하는 후크입니다. 다른 것을 원한다면 코드에 명시 적으로 작성하십시오.


2
C ++의 경우 "메모리 레이아웃"과 관련이 없습니다. Python이 제공하는 것과 동일한 초기화 모델이 C ++에서 구현되었을 수 있습니다. 건설 / 파괴가 C ++에있는 방식 인 유일한 이유는 리소스 관리 (RAII)를위한 신뢰할 수 있고 올바르게 작동하는 기능을 제공하기위한 디자인 결정 때문입니다 (자동으로 생성 될 수 있음) (코드 및 인적 오류 감소) 그들에 대한 규칙 (그들의 호출 순서)이 엄격하게 정의되어 있기 때문에 컴파일러에 의해. 확실하지는 않지만 Java는이 접근법을 따라 POLA C와 같은 또 다른 언어 일 수 있습니다.
Alexander Shukaev

10

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

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


8
나는 대부분 당신에게 동의하지만 Java의 규칙은 실제로 매우 간단합니다. 특별히 다른 것을 요구하지 않으면 인수 없음 생성자가 호출됩니다.
Laurence Gonsalves

1
@Laurence-부모 클래스가 인수가없는 생성자를 정의하지 않으면 어떻게됩니까? 인수없는 생성자가 보호되거나 개인 인 경우 어떻게됩니까?
Mike Axiak

8
명시 적으로 호출하려고 할 때와 똑같은 일이 발생합니다.
Laurence Gonsalves

8

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


7

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

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


이것이 정답이었습니다. 파이썬은 서브 클래스와 수퍼 클래스간에 전달되는 인수의 의미론을 정의해야합니다. 이 답변은 찬성되지 않습니다. 어쩌면 몇 가지 예에서 문제가 있었습니까?
Gary Weiss

5

혼동을 피하기 위해 __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.

3

어쩌면 __init__서브 클래스가 오버라이드 (override) 할 필요가있는 방법이다. 때때로 서브 클래스는 클래스 별 코드를 추가하기 전에 부모의 함수를 실행해야하며, 다른 경우에는 부모의 함수를 호출하기 전에 인스턴스 변수를 설정해야합니다. 파이썬이 언제 함수를 호출하는 것이 가장 적합한지를 알 수있는 방법이 없기 때문에 추측해서는 안됩니다.

만약 당신이 흔들리지 않는다면, 그것은 __init__또 다른 기능 이라는 것을 고려하십시오 . 문제의 함수가 dostuff대신 파이썬이 부모 클래스에서 해당 함수를 자동으로 호출하도록 하시겠습니까?


2

여기서 가장 중요한 고려 사항은을 자동 호출하면 super.__init__()디자인, 초기화 방법이 호출되는 시점 및 인수를 지정하는 것입니다. 이를 자동으로 호출하고 프로그래머가 명시 적으로 해당 호출을 수행하도록 요구하면 많은 유연성이 필요합니다.

결국, 클래스 B가 클래스 A에서 파생되었다고 A.__init__()해서와 동일한 인수로 호출 할 수 있거나 호출해야 한다는 의미는 아닙니다 B.__init__(). 호출을 명시 적으로 만드는 것은 프로그래머가 예를 들어 B.__init__()완전히 다른 매개 변수로 정의 하고, 해당 데이터로 계산 A.__init__()을 수행하고, 해당 메소드에 적합한 인수를 호출 한 후 일부 후 처리를 수행 할 수 있음을 의미합니다. 이러한 종류의 유연성은 실행 직전 또는 직후에 암시 적 A.__init__()으로 호출되는 경우 달성하기 어색 합니다.B.__init__()B.__init__()

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