파이썬의 상속은“is-a”상속 스타일입니까 아니면 구성 스타일입니까?


10

파이썬이 다중 상속을 허용한다는 것을 감안할 때 파이썬의 관용 상속은 어떻게 생겼습니까?

Java와 같은 단일 상속을 가진 언어에서 상속은 하나의 객체가 다른 객체의 "is-a"라고 말하고 객체간에 코드를 공유하려고 할 때 사용됩니다 (부모 객체에서 자식 객체로). 예를 들어, 당신은 말할 수 DogA는 Animal:

public class Animal {...}
public class Dog extends Animal {...}

그러나 파이썬은 다중 상속을 지원하므로 다른 많은 객체를 함께 구성하여 객체를 만들 수 있습니다. 아래 예를 고려하십시오.

class UserService(object):
    def validate_credentials(self, username, password):
        # validate the user credentials are correct
        pass


class LoggingService(object):
    def log_error(self, error):
        # log an error
        pass


class User(UserService, LoggingService):
    def __init__(self, username, password):
        self.username = username
        self.password = password

    def authenticate(self):
        if not super().validate_credentials(self.username, self.password):
            super().log_error('Invalid credentials supplied')
            return False
         return True

파이썬에서 다중 상속을 허용하거나 잘 사용합니까? 상속은 하나의 객체가 다른 객체의 "is-a"일 때 대신에 와 User로 구성된 모델 을 만듭니다 .UserServiceLoggingService

데이터베이스 또는 네트워크 작업에 대한 모든 논리 UserUserService개체 에 넣고 모델에 로그인하기위한 모든 논리를 유지 하여 모델 과 별도로 유지할 수 있습니다 LoggingService.

이 접근 방식의 일부 문제는 다음과 같습니다.

  • 이것이 신의 대상을 창조 하는가? 이후 User의 상속 또는 구성되어, UserService그리고 LoggingService정말 단일 책임의 원칙을 따르고있다?
  • 부모 / 다음 인라인 객체의 메소드에 액세스하려면 (예 : UserService.validate_credentials을 사용해야 super합니다.)이 메소드를 처리 할 객체를 확인하기가 조금 더 어렵고 명확하지 않습니다. , 인스턴스화 UserService및 같은 일을self.user_service.validate_credentials

위의 코드를 구현하는 Pythonic 방법은 무엇입니까?

답변:


9

파이썬의 상속은“is-a”상속 스타일입니까 아니면 구성 스타일입니까?

파이썬은 두 스타일을 모두 지원합니다. 사용자가 한 소스의 로깅 기능과 다른 소스의 신임 정보 유효성 검증을 갖는 컴포지션의 관계를 설명하고 있습니다. LoggingServiceUserService기지 유지 mixin 있습니다 : 그들은 기능을 제공하고 스스로 인스턴스화되는 것은 아니다.

유형으로 믹스 인을 구성하면 로그 할 수 있지만 자신의 인스턴스화 기능을 추가해야하는 사용자가 있습니다.

그렇다고해서 단일 상속을 고수 할 수는 없습니다. 파이썬도 그것을 지원합니다. 다중 상속의 복잡성으로 인해 개발 능력이 방해를 받으면 더 편안하게 느끼거나 디자인의 가치가 있다고 생각할 때까지 피할 수 있습니다.

이것이 신의 대상을 창조 하는가?

로깅은 다소 접선으로 보입니다-파이썬에는 로거 객체가있는 자체 로깅 모듈이 있으며 규칙은 모듈 당 하나의 로거가 있다는 것입니다.

그러나 로깅 모듈을 따로 설정하십시오. 아마도 이것은 단일 책임을 위반할 수도 있지만 특정 상황에서 사용자를 정의하는 것이 중요합니다. 책임을 취소하는 것은 논란의 여지가 있습니다. 그러나 더 넓은 원칙은 파이썬이 사용자가 결정을 내릴 수 있도록한다는 것입니다.

슈퍼가 덜 명확합니까?

super같은 이름의 함수 안에서 MRO (Method Resolution Order)에서 부모에게 위임해야 할 때만 필요합니다. 부모의 메소드에 대한 호출을 하드 코딩하는 대신이를 사용하는 것이 가장 좋습니다. 그러나 부모를 하드 코딩하지 않으려는 경우 필요하지 않습니다 super.

여기 예제에서는 수행해야합니다 self.validate_credentials. self당신의 관점에서 더 명확하지 않습니다. 둘 다 MRO를 따릅니다. 나는 적절한 곳에서 단순히 각각을 사용합니다.

당신이 호출 한 경우 authenticate, validate_credentials대신에, 당신이 사용하는 데 필요한 것 super(또는 하드 코드 부모)는 재귀 오류를 방지 할 수 있습니다.

대체 코드 제안

따라서 의미가 OK라고 가정하면 (로깅과 같이) 클래스에서 수행하는 작업은 다음과 User같습니다.

    def validate_credentials(self): # changed from "authenticate" to 
                                    # demonstrate need for super
        if not super().validate_credentials(self.username, self.password):
            # just use self on next call, no need for super:
            self.log_error('Invalid credentials supplied') 
            return False
        return True

1
동의하지 않습니다. 상속은 항상 서브 클래스에 클래스의 공용 인터페이스 사본을 작성합니다. 이것은 "has-a"관계가 아닙니다. 이것은 단순하고 단순하며 하위 설명이므로 설명 된 응용 프로그램에는 적합하지 않습니다.
Jules

@Jules 당신은 무엇에 동의하지 않습니까? 나는 설명 할 수있는 많은 것들을 말했고 논리적으로 따르는 결론을 내렸다. 당신은 이다 당신이 말할 때 잘못된 "상속은 항상 그 서브 클래스의 클래스의 공용 인터페이스의 복사본을 만듭니다." 파이썬에서는 사본이 없습니다. 메소드는 C3 알고리즘 메소드 분석 순서 (MRO)에 따라 동적으로 조회됩니다.
Aaron Hall

1
요점은 구현의 작동 방식에 대한 특정 세부 정보가 아니라 클래스의 공개 인터페이스가 어떻게 보이는지에 관한 것입니다. 예제의 경우 User객체는 인터페이스에 User클래스에 정의 된 멤버 뿐만 아니라 UserService및에 정의 된 멤버 도 있습니다 LoggingService. 이 아닌 공용 인터페이스 (직접적인 복사를 통해, 오히려 수퍼 '인터페이스 간접 조회하여이기는)에 복사되기 때문에,은 "보유-A'의 관계.
Jules

Has-a는 구성을 의미합니다. 믹스 인은 구성의 형태입니다. 사용자 클래스 입니다 UserService 또는 LoggingService하지,하지만 그 기능을. 파이썬의 상속은 당신이 알고있는 것보다 Java와 더 다르다고 생각합니다.
Aaron Hall

@AaronHall 당신은 지나치게 단순화하고 있습니다 (이것은 우연히 발견 된 다른 답변 과 모순되는 경향이 있습니다 ). 하위 유형 관계의 관점에서 사용자는 UserService 및 LoggingService입니다. 이제 여기서의 정신은 사용자가 그러한 기능을 갖도록 기능을 구성하는 것입니다. 믹스 인은 일반적으로 다중 상속으로 구현할 필요가 없습니다. 그러나 이것은 파이썬에서 그렇게하는 일반적인 방법입니다.
coredump

-1

여러 개의 수퍼 클래스를 허용한다는 사실 외에, 파이썬의 상속은 Java와 실질적으로 다르지 않습니다. 즉, 서브 클래스의 멤버도 각 슈퍼 타입의 멤버입니다 [1]. 파이썬이 덕 타이핑을 사용한다는 사실은 차이가 없습니다. 하위 클래스에는 모든 수퍼 클래스 멤버가 있으므로 해당 서브 클래스를 사용할 수있는 모든 코드에서 사용할 수 있습니다. 컴포지션을 사용하여 다중 상속이 효과적으로 구현된다는 사실은 빨간 청어입니다. 한 클래스의 속성을 다른 클래스로 자동 복사하는 것이 문제이며 컴포지션을 사용하는지 또는 멤버가 어떻게 추측되는지 마술로 추측하는 것은 중요하지 않습니다 일하는 것 : 그것들을 갖는 것이 잘못되었습니다.

그렇습니다. 객체가 논리적으로 설계된 작업의 일부가 아닌 작업을 수행 할 수있는 기능을 제공하기 때문에 이는 단일 책임을 위반하는 것입니다. 그렇습니다. "신 (神)"객체를 생성하는데, 이것은 본질적으로 같은 것을 말하는 또 다른 방법입니다.

파이썬에서 객체 지향 시스템을 설계 할 때 Java 설계 서적에서 설교 한 것과 동일한 최대 값도 적용됩니다. 상속보다 구성을 선호하십시오. 다중 상속을 가진 다른 시스템들 (대부분 [2])도 마찬가지입니다.

[1] : 실제 세계를 모델링한다는 아이디어를 제안하기 때문에 개인적으로 용어를 좋아하지는 않지만 "is-a"관계라고 할 수 있으며 객체 지향 모델링은 실제 세계와 동일하지 않습니다.

[2] : C ++에 대해서는 잘 모르겠습니다. C ++는 상속 된 클래스의 공용 멤버를 사용하려는 경우 필드 이름을 지정할 필요없이 본질적으로 구성되는 "개인 상속"을 지원합니다. 클래스의 공개 인터페이스에는 전혀 영향을 미치지 않습니다. 난 몰라 처럼 그것을 사용,하지만 난하지 어떤 좋은 이유를 볼 수 없습니다.

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