나는 Raymond Hettinger의 Pycon 강연 "슈퍼 고려 슈퍼"를보고 결정적인 방법으로 클래스 "부모"클래스를 선형화하는 Python의 MRO (Method Resolution Order)에 대해 조금 배웠습니다. 우리는 이것을 아래 코드와 같이 의존성 주입을 위해 우리의 이점으로 사용할 수 있습니다. 이제 자연스럽게 super
모든 것에 사용하고 싶습니다 !
아래의 예에서 User
클래스에서 모두 상속하여 그것의 종속성을 선언 LoggingService
하고 UserService
. 이것은 특별히 특별하지 않습니다. 흥미로운 부분은 Method Resolution Order를 사용하여 단위 테스트 중에 종속성을 모을 수 있다는 것입니다. 아래 코드 MockUserService
는를 상속 UserService
하고 우리가 조롱하려는 메소드의 구현을 제공합니다. 아래 예에서는의 구현을 제공합니다 validate_credentials
. 이하기 위해 MockUserService
어떤 통화를 처리 validate_credentials
우리가 전에 배치 할 필요가 UserService
MRO한다. 이 래퍼 클래스를 생성하여 수행 User
전화를 MockUser
하고에서를 상속 가지고 User
와 MockUserService
.
이제 우리가 할 때 메소드 MockUser.authenticate
호출 순서에 super().validate_credentials()
MockUserService
앞서 호출 이 이루어 UserService
지며, validate_credentials
이 구현 의 구체적인 구현을 제공 하므로 사용됩니다. 예, UserService
단위 테스트에서 성공적으로 조롱했습니다 . UserService
고가의 네트워크 또는 데이터베이스 호출을 수행 할 수 있다는 점을 고려하십시오 . 대기 시간 요소를 제거했습니다. UserService
라이브 / 프로드 데이터 를 만질 위험도 없습니다 .
class LoggingService(object):
"""
Just a contrived logging class for demonstration purposes
"""
def log_error(self, error):
pass
class UserService(object):
"""
Provide a method to authenticate the user by performing some expensive DB or network operation.
"""
def validate_credentials(self, username, password):
print('> UserService::validate_credentials')
return username == 'iainjames88' and password == 'secret'
class User(LoggingService, UserService):
"""
A User model class for demonstration purposes. In production, this code authenticates user credentials by calling
super().validate_credentials and having the MRO resolve which class should handle this call.
"""
def __init__(self, username, password):
self.username = username
self.password = password
def authenticate(self):
if super().validate_credentials(self.username, self.password):
return True
super().log_error('Incorrect username/password combination')
return False
class MockUserService(UserService):
"""
Provide an implementation for validate_credentials() method. Now, calls from super() stop here when part of MRO.
"""
def validate_credentials(self, username, password):
print('> MockUserService::validate_credentials')
return True
class MockUser(User, MockUserService):
"""
A wrapper class around User to change it's MRO so that MockUserService is injected before UserService.
"""
pass
if __name__ == '__main__':
# Normal useage of the User class which uses UserService to resolve super().validate_credentials() calls.
user = User('iainjames88', 'secret')
print(user.authenticate())
# Use the wrapper class MockUser which positions the MockUserService before UserService in the MRO. Since the class
# MockUserService provides an implementation for validate_credentials() calls to super().validate_credentials() from
# MockUser class will be resolved by MockUserService and not passed to the next in line.
mock_user = MockUser('iainjames88', 'secret')
print(mock_user.authenticate())
이것은 매우 영리한 느낌이지만 파이썬의 다중 상속 및 메소드 해결 순서를 적절하고 올바르게 사용합니까? Java로 OOP를 배운 방식으로 상속에 대해 생각할 때 이것은 User
a UserService
또는 User
is 라고 말할 수 없기 때문에 완전히 잘못 느낍니다 LoggingService
. 그런 식으로 생각하고 상속을 사용하여 위의 코드에서 사용하는 방식은별로 의미가 없습니다. 아니면? 상속을 사용하여 코드 재사용을 제공하고 부모-> 자녀 관계라는 관점에서 생각하지 않는다면 그렇게 나쁘지 않습니다.
내가 잘못하고 있습니까?