오늘은 어제 수업시간에서 DRF-simplejwt의 RefreshToken을 보고 생긴 궁금증인 MRO에 대해서 알아보려고 한다.
class RefreshToken(BlacklistMixin["RefreshToken"], Token):
pass
우선 위의 RefreshToken 클래스를 보면 BlacklistMixin과 Token 두 개의 클래스를 상속받고 있단는 것을 알 수 있다.
어제 RefreshToken으로 인스턴스를 생성했을 때, 자동으로 check_blacklist() 메소드가 실행되었는데
대체 어디 부분에서 check_blacklist()메소드가 실행되는지를 찾아보려고 했다.
class BlacklistMixin(Generic[T]):
if "rest_framework_simplejwt.token_blacklist" in settings.INSTALLED_APPS:
def verify(self, *args, **kwargs) -> None:
self.check_blacklist()
super().verify(*args, **kwargs) # type: ignore
def check_blacklist(self) -> None:
"""
Checks if this token is present in the token blacklist. Raises
`TokenError` if so.
"""
jti = self.payload[api_settings.JTI_CLAIM]
if BlacklistedToken.objects.filter(token__jti=jti).exists():
raise TokenError(_("Token is blacklisted"))
check_blacklist()를 찾아서 왔더니 BlacklistMixin 에서 발견할 수 있었고,
verify() 가 실행되면 self.check_blacklist()를 통해서 블랙리스트 검증이 일어난다는 것을 알 수 있다.
그럼 verify()를 실행해주는 곳은 어디에 있는거지?
class Token:
def __init__(self, token: Optional["Token"] = None, verify: bool = True) -> None:
if token is not None:
# Decode token
try:
self.payload = token_backend.decode(token, verify=verify)
except TokenBackendError:
raise TokenError(_("Token is invalid or expired"))
if verify:
self.verify()
def verify(self) -> None:
self.check_exp()
if (
api_settings.JTI_CLAIM is not None
and api_settings.JTI_CLAIM not in self.payload
):
raise TokenError(_("Token has no id"))
if api_settings.TOKEN_TYPE_CLAIM is not None:
self.verify_token_type()
우선은 긴 코드지만 쉽게 보기 위해서 필요한 부분만을 가져왔다.
코드를 보면 또 다른 상속을 해주는 class인 Token에서 생성자에 의해서 인스턴스가 생성될 때, verify를 실행시켜준다는 것을 알 수 있다.
하지만 self.verify()를 실행하면 Token의 verify가 실행되어야 하는데 왜 BlacklistMixin의 verify가 실행이 될까?
여기서 알아야하는 것이 Python MRO이다.
Python MRO(Method Resolution Order)
Python에서 MRO는 다중 상속 시 메소드나 속성을 어떤 순서로 검색할지를 결정하는 규칙을 의미한다.
예제를 보자
class A:
def __init__(self):
self.say()
def say(self):
print("say A")
class C:
def say(self):
print("say C")
class AC(A, C):
pass
class CA(C, A):
pass
ac, ca = AC(), CA()
ac와 ca는 각각 어떤 say를 print할까?
생성자는 둘다 class A의 생성자를 상속받아 실행하지만 ca의 경우에는 C를 먼저 검사하여 say를 찾는 것을 볼 수 있다.
이것처럼 상속도 순서를 두어서 먼저 상속받은 클래스를 들어가서 "너는 say를 가지고 있니?"라며 찾는 것이다.
예제를 하나 더 살펴보도록 하자.
class MixinTest:
def verify(self):
print("MixinTest의 verify 실행")
class OtherClass:
def __init__(self):
print(self)
self.verify()
def verify(self):
print("OtherClass의 verify 실행")
class MyClass(MixinTest, OtherClass):
pass
my_class = MyClass()
위의 RefreshToken에 대해서 잘 알도록 비슷하게 구성을 해보았다.
실행 결과는 다음과 같은데 여기서도 MyClass의 인스턴스를 하나 생성하면 상속받은 OtherClass의 생성자를 실행한다.
print(self)는 MyClass 자신을 나타내며 self.verify()를 찾아야 하는데
먼저 MyClass를 봤는데 어디에도 verify가 없으니 다음 순서인 MixinTest로 가서 찾는다.
찾아보니까 verify를 가지고 있으니 여기에 있는 verify를 실행하면서 코드를 마치는 것이다.
그렇다면 위에서도 봤듯이 Token보다 BlacklistMixin이 앞에 있으니까 RefreshToken안에 없으니
BlacklistMixin에 가서 찾아보았고 verify가 있으니 실행하면서 check_blacklist()도 실행한 것이다.
이처럼 클래스에 매핑되는 순서는 상속 구조에 따라 달라지는데 이를 확인하기 위해서 __mro__로 확인하는 방법이 있다고 한다.
'Python' 카테고리의 다른 글
[Python] OS 알아보기 (1) | 2024.09.20 |
---|---|
[Python] string 모듈에 대해서 (0) | 2024.07.10 |
[Python] 매직 메소드(special method) (0) | 2024.07.09 |
[Python] map, lambda (0) | 2024.07.08 |
[Python] Class에 대한 이해 (0) | 2024.07.05 |