파이썬의 상속은“is-a”상속 스타일입니까 아니면 구성 스타일입니까? # validate the user credentials are

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

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 방법은 무엇입니까?



답변

파이썬의 상속은“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

답변

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

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

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

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

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