programing

superclass __init_ 메서드가 자동으로 호출되지 않는 이유는 무엇입니까?

bestprogram 2023. 6. 21. 22:49

superclass __init_ 메서드가 자동으로 호출되지 않는 이유는 무엇입니까?

subclasses의 Python을 결정했을까요?__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__ 자체로 예를 들어!-).

저는 사람들이 "파이썬의 젠"을 앵무새처럼 흉내낼 때 약간 당황스럽습니다.이것은 디자인 철학입니다. 특정한 디자인 결정은 항상 더 구체적인 용어로 설명될 수 있습니다. 그렇지 않으면 "파이썬의 젠"은 무엇이든 할 수 있는 구실이 됩니다.

그 이유는 간단합니다. 기본 클래스를 구성하는 방법과 유사한 방식으로 파생 클래스를 구성할 필요는 없습니다.모수가 더 많거나 더 적거나 순서가 다르거나 전혀 관련이 없을 수 있습니다.

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++에서는 메모리 레이아웃 때문에 기본 클래스 생성자를 호출해야 합니다.

BaseClassfield1그리고 당신은 새로운 클래스를 만듭니다.SubClass 구성을니다합원추가다니▁that▁adds.field2에 그다의예의 예.SubClass에는 에대공간을 위한 이 포함되어 있습니다.field1그리고.field2은 의생자필다니합의 합니다.BaseClass보충하기 위해field1되는 모든 반복하도록 한BaseClass자체 생성자에서 초기화합니다.그리고 만약에field1비공개인 경우 상속 클래스를 초기화할 수 없습니다.field1.

Python은 Java 또는 C++이 아닙니다.모든 사용자 정의 클래스의 모든 인스턴스는 동일한 '모양'을 가집니다.기본적으로 속성을 삽입할 수 있는 사전일 뿐입니다.초기화가 완료되기 전에 모든 사용자 정의 클래스의 모든 인스턴스는 거의 동일합니다. 아직 저장되지 않은 속성을 저장하는 위치일 뿐입니다.

따라서 Python 하위 클래스는 기본 클래스 생성자를 호출하지 않는 것이 좋습니다.원한다면 속성 자체를 추가할 수도 있습니다.에 대해 , 추가된 코드 BaseClass 및 은 속코의드추다니해습가에서 되었습니다.SubClass방법.

흔히 있는 일이지만,SubClass실제로 모든 것을 갖고 싶어합니다.BaseClass하기 전에 입니다. 을라고 부르면 됩니다. 그러면 예, 그냥 전화하시면 됩니다.BaseClass.__init__()(또는 사용)super하지만 그것은 복잡하고 때때로 고유한 문제가 있습니다.)하지만 당신은 꼭 그러지 않아도 돼요.그리고 여러분은 그것을 이전, 이후, 또는 다른 주장으로 할 수 있습니다.당신이 원한다면, 당신은 전화할 수 있습니다.BaseClass.__init__ 다른 방법으로__init__아마도 당신은 이상하고 게으른 초기화 작업을 하고 있을 것입니다.

Python은 단순성을 유지함으로써 이러한 유연성을 달성합니다.다음을 작성하여 개체를 초기화합니다.__init__ 는방법에 self 그거야바로 그겁니다.메소드와 동일하게 동작합니다. 메소드이기 때문입니다.먼저 해야 할 일이나 다른 일을 하지 않으면 자동으로 일어날 일에 대한 이상하고 직관적이지 않은 규칙은 없습니다.이 기능을 수행해야 하는 유일한 목적은 객체 초기화 중에 실행할 후크로 초기 속성 값을 설정하는 것입니다.다른 작업을 수행하려면 코드에 명시적으로 기록해야 합니다.

을 피하기 위해 base_class를 할 수 을 아는 것이 유용합니다.__init__()에 child_class가 __init__()학급.

예:

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__()메서드가 자식 클래스에 있습니다.

예를 들어 다음 코드가 오류를 반환합니다. 클래스 부모: definit(자체, a=1, b=0): self.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.

"명시적인 것이 암시적인 것보다 낫습니다."그것은 우리가 '자신'을 명시적으로 써야 한다는 것을 나타내는 것과 같은 추론입니다.

저는 결국 그것이 이익이라고 생각합니다. 자바가 슈퍼클래스의 건설자를 부르는 것과 관련하여 가지고 있는 모든 규칙을 암송할 수 있습니까?

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

생성자가 자동으로 호출된 경우, 발생 순서를 설명하는 길이가 같은 다른 페이지가 필요합니다.그건 지옥이 될 거예요...

종종 하위 클래스에는 슈퍼 클래스에 전달할 수 없는 추가 매개 변수가 있습니다.

아마도요.__init__하위 클래스가 재정의해야 하는 메서드입니다.때때로 하위 클래스는 클래스별 코드를 추가하기 전에 부모 함수를 실행해야 하고, 다른 경우에는 부모 함수를 호출하기 전에 인스턴스 변수를 설정해야 합니다.Python은 언제 이러한 기능을 호출하는 것이 가장 적합한지 알 수 있는 방법이 없으므로 추측해서는 안 됩니다.

만약 그것들이 당신을 동요시키지 않는다면, 그것을 고려하세요.__init__다른 기능일 뿐입니다.문제의 기능이 다음과 같다면,dostuff대신 Python이 부모 클래스의 해당 함수를 자동으로 호출하기를 원하십니까?

저는 여기서 매우 중요한 고려 사항 중 하나는 자동 전화를 사용하는 것이라고 생각합니다.super.__init__()으로 호출하는 것을 피하고 프로그래머가 명시적으로 호출하도록 요구하는 것은 많은 유연성을 자동으로 호출하는 것을 피하고 프로그래머가 그 호출을 명시적으로 하도록 요구하는 것은 많은 유연성을 수반합니다.

결국, 클래스 B가 클래스 A로부터 파생되었다고 해서 그것이 의미하는 것은 아닙니다.A.__init__() 인수호출수있호합니다출야해거나할과 한 인수로 할 수 .B.__init__() 것은 할 수 것을 예를 정의합니다.B.__init__() 다른 변수를 하여 해당합니다. " 완히다 매변사여하용를다고그수니전약하계행, "화합터로의간을산이데전른수"A.__init__()해당 방법에 적합한 인수를 사용한 다음 후 처리를 수행합니다.이런 종류의 유연성은 달성하기가 어색할 것입니다.A.__init__()에서 호출될 것입니다.B.__init__()묵적으로이 에, 은전에혹암.B.__init__()실행 또는 바로 뒤에 표시됩니다.

세르게이 오르샨스키가 댓글에서 지적했듯이, 장식가를 쓰는 것도 편리합니다.__init__방법.

다음을 상속할 장식자를 작성할 수 있습니다.__init__메소드, 그리고 심지어 자동으로 하위 클래스를 검색하고 장식합니다.2015년 9월 9일 23시 17분에 세르게이 오르샨스키.

1/3부:구현

참고: 실제로는 기본 클래스와 파생 클래스 모두를 호출하려는 경우에만 유용합니다.__init__ 이래__init__자동으로 상속됩니다.이 질문에 대한 이전 답변을 참조하십시오.

def default_init(func):
    def wrapper(self, *args, **kwargs) -> None:
        super(type(self), self).__init__(*args, **kwargs)
    return wrapper

class base():
    def __init__(self, n: int) -> None:
        print(f'Base: {n}')

class child(base):
    @default_init
    def __init__(self, n: int) -> None:
        pass
        
child(42)

출력:

Base: 42

2/3부: 경고

경고: 다음 경우에는 작동하지 않습니다.base가 라고불그자체는리자체▁called라고 불립니다.super(type(self), self).

def default_init(func):
    def wrapper(self, *args, **kwargs) -> None:
        '''Warning: recursive calls.'''
        super(type(self), self).__init__(*args, **kwargs)
    return wrapper

class base():
    def __init__(self, n: int) -> None:
        print(f'Base: {n}')

class child(base):
    @default_init
    def __init__(self, n: int) -> None:
        pass
        
class child2(child):
    @default_init
    def __init__(self, n: int) -> None:
        pass
        
child2(42)

재귀 오류: Python 개체를 호출하는 동안 최대 재귀 깊이를 초과했습니다.

: 3/3부:그냥 플레인을 사용하는 게 어때요?super()?

하지만 한 플레인을 요?super()새로 리바인딩한 이후로 작동하지 않기 때문입니다.__init__반 , 반밖서왔고그고에리그, 고,▁is▁outside왔▁from고리▁and.super(type(self), self)필수 항목입니다.

def default_init(func):
    def wrapper(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
    return wrapper

class base():
    def __init__(self, n: int) -> None:
        print(f'Base: {n}')

class child(base):
    @default_init
    def __init__(self, n: int) -> None:
        pass
        
child(42)

오류:

---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-9-6f580b3839cd> in <module>
     13         pass
     14 
---> 15 child(42)

<ipython-input-9-6f580b3839cd> in wrapper(self, *args, **kwargs)
      1 def default_init(func):
      2     def wrapper(self, *args, **kwargs) -> None:
----> 3         super().__init__(*args, **kwargs)
      4     return wrapper
      5 

RuntimeError: super(): __class__ cell not found

배경 - 부모 및 자식 클래스를 자동으로 시작할 수 있습니다!

여기에 많은 대답이 있고 "이것은 파이썬 방식이 아닙니다, 사용하세요.super().__init__()하위 클래스로부터."문제는 비단뱀의 방법을 묻는 것이 아니라, 다른 언어의 예상 행동과 명백히 다른 비단뱀의 행동을 비교하는 것입니다.

MRO 문서는 예쁘고 다채롭지만 실제로 TLDR 상황이며 이러한 유형의 비교에서 흔히 볼 수 있는 "Do do the Python way, because"와 같이 여전히 질문에 제대로 대답하지 못합니다.

@keyvanrm의 패턴 빌드인 하위 클래스의 나중 선언에 의해 상속된 개체가 오버로드될 수 있습니다(https://stackoverflow.com/a/46943772/1112676) 응답은 명시적으로 호출하지 않고 클래스를 호출하는 일부로 자동으로 부모 클래스를 시작하려는 경우를 해결합니다.super().__init__()모든 아동 학급에서

새로운 팀원에게 보일러 플레이트 모듈 템플릿(핵심 애플리케이션 소스를 건드리지 않고 애플리케이션을 확장하기 위해)을 사용하도록 요청할 수 있는 경우, 기본 기계를 모르거나 이해할 필요 없이 쉽게 채택할 수 있습니다.잘 문서화된 응용프로그램의 기본 인터페이스.

"묵시적인 것이 암시적인 것보다 낫다"고 말할 사람들을 위해.그러나 일반적으로 동의합니다. 다른 많은 인기 있는 언어에서 상속된 자동 초기화는 예상되는 동작이며 일부는 핵심 응용 프로그램에서 작업하고 다른 일부는 이를 확장하는 프로젝트에 활용할 수 있다면 매우 유용합니다.

이 기술은 init에 대한 인수/키워드 인수를 전달할 수도 있습니다. 이는 거의 모든 개체가 상위 클래스 또는 상위 클래스에 의해 푸시되어 사용될 수 있음을 의미합니다.

예:


class Parent:
    def __init__(self, *args, **kwargs):
        self.somevar = "test"
        self.anothervar = "anothertest"

        #important part, call the init surrogate pass through args:
        self._init(*args, **kwargs)

    #important part, a placeholder init surrogate:
    def _init(self, *args, **kwargs):
        print("Parent class _init; ", self, args, kwargs)

    def some_base_method(self):
        print("some base method in Parent")
        self.a_new_dict={}


class Child1(Parent):
    # when omitted, the parent class's __init__() is run
    #def __init__(self):
    #    pass

    #overloading the parent class's  _init() surrogate
    def _init(self, *args, **kwargs):
        print(f"Child1 class _init() overload; ",self, args, kwargs)

        self.a_var_set_from_child = "This is a new var!"


class Child2(Parent):
    def __init__(self, onevar, twovar, akeyword):
        print(f"Child2 class __init__() overload; ", self)

        #call some_base_method from parent
        self.some_base_method()

        #the parent's base method set a_new_dict
        print(self.a_new_dict)


class Child3(Parent):
    pass


print("\nRunning Parent()")
Parent()
Parent("a string", "something else", akeyword="a kwarg")

print("\nRunning Child1(), keep Parent.__init__(), overload surrogate Parent._init()")
Child1()
Child1("a string", "something else", akeyword="a kwarg")

print("\nRunning Child2(), overload Parent.__init__()")
#Child2() # __init__() requires arguments
Child2("a string", "something else", akeyword="a kwarg")

print("\nRunning Child3(), empty class, inherits everything")
Child3().some_base_method()

출력:

Running Parent()
Parent class _init;  <__main__.Parent object at 0x7f84a721fdc0> () {}
Parent class _init;  <__main__.Parent object at 0x7f84a721fdc0> ('a string', 'something else') {'akeyword': 'a kwarg'}

Running Child1(), keep Parent.__init__(), overload surrogate Parent._init()
Child1 class _init() overload;  <__main__.Child1 object at 0x7f84a721fdc0> () {}
Child1 class _init() overload;  <__main__.Child1 object at 0x7f84a721fdc0> ('a string', 'something else') {'akeyword': 'a kwarg'}

Running Child2(), overload Parent.__init__()
Child2 class __init__() overload;  <__main__.Child2 object at 0x7f84a721fdc0>
some base method in Parent
{}

Running Child3(), empty class, inherits everything, access things set by other children
Parent class _init;  <__main__.Child3 object at 0x7f84a721fdc0> () {}
some base method in Parent

오버로드된 정의는 상위 클래스에서 선언된 정의를 대신하지만 상위 클래스에서 호출할 수 있으므로 하위 클래스에서 상위 클래스의 init()를 명시적으로 호출할 필요 없이 부모 및 자식 클래스가 모두 초기화되는 고전적인 암시적 상속 초기화 동작을 에뮬레이트할 수 있습니다.

개인적으로 _init를 _init() 라고 부릅니다.main()를 들어 하위 으로 선언된 인 "C++" Python"에 .main()즉)

언급URL : https://stackoverflow.com/questions/3782827/why-arent-superclass-init-methods-automatically-invoked