이전에 작성한 포스트와 코드에 대해서 복습 겸 리뷰를 해 보려고 한다. 2시간도 안 걸려서 작성한 코드를 복습할 게 있나 싶을 순 있는데, 짧게 작성하면서도 나름 모르는 부분이나 주먹구구식으로 작성한 부분이 보여서 짚고 가면 좋을 것 같았다.
✅ def main()에서 if __name__ == '__main__'인 것과 def main 없이 바깥에서 실행하는 것은 무슨 차이일까
생각해보니 별 차이는 없을 것 같다. if문으로 분기를 했냐, 안 했냐의 차이일 뿐이기 때문이다. 그렇다면 if문 없이 그냥 main() 함수를 호출하면 되지 않나 싶기도 하다. 이랬을 때의 차이가 있을까?
추측
파일을 실행하는 명령어에 차이가 있을 수도 있겠다. __name__ 이라는 변수가 파일의 절대 경로인 것 같은데, 만약 코드를 다르게 작성한다면 'python main.py' 명령어를 이용했을 때 if문에 걸리지 않을 수도 있겠다.
얻어낸 답
import를 할 때 파일이 어떻게 실행되느냐의 차이라고 한다. '__name__' 이라는 변수는 파일마다 존재하는 변수로써, 해당 파일이 어떻게 실행되는지에 따라서 값이 달라진다고 한다. 만약 해당 파일이 'python main.py' 처럼 스크립트로 직접 실행된다면, 해당 변수(__name__)의 값은 '__main__'이 된다. 그러나 main.py 파일이 직접 실행되지 않고 다른 파일에 의해 import 되어서 사용된다면, 해당 파일의 __name__ 변수의 값은 'main', 즉 해당 파일의 이름이 된다.
만약 if문으로 분기 처리를 하지 않았다면, main 파일/모듈이 다른 파일/모듈에서 import 되는 경우에도 main 함수(main 파일 안의 main 함수)가 실행될 수 있다. 그러나 위의 방식으로 코드를 작성함으로써, main 함수를 정의하였지만 main.py 파일을 '직접 실행'할 경우에만 main 함수를 실행하도록 구분할 수 있겠다.
✅ abc 패키지의 ABC와 abstractmethod의 동작 원리가 궁금하다.
abc라는 모듈은 사실 abstract base class의 약자였다. abc가 마냥 java의 interface와 같은 역할을 한다고만 생각했는데, 생각해보니 abc의 정의도 잘 몰랐다. 파이썬 공식문서와 Geeksforgeeks 문서, stackoverflow를 참고한 결과 abc는 파이썬에서 interface를 사용하도록 해 주는 여러 방법 중 하나라는 결론을 내렸다.
파이썬 공식문서에서는 abc를 인터페이스를 제공함으로써 duck typing(이게 뭔지 모른다...)을 가능하게 해 주는 클래스라고 설명했는데 잘 와닿지 않았다. 그것보단 geeksforgeeks에서 '클래스이지만 그 클래스 혼자 초기화될 수는 없고, 반드시 다른 클래스의 상속 등을 통해서 초기화될 수 있는 클래스'라는 설명이 더 와닿았다.
이제 abc가 혼자서 초기화될 수 없는 클래스라는 건 알았다. 그런데 자바에서는 혼자서 초기화될 수 없는 경우는 '추상 클래스'와 '인터페이스' 두 가지가 있다. 파이썬에서는 이러한 개념이 없는 것일까? 이에 대한 설명도 stackoverflow에 나와 있더라.

파이썬과 자바의 상속 방식이나 규칙이 달랐다. 자바의 경우 클래스와 인터페이스가 명확하게 구분되며, 하나의 클래스는 하나의 추상 클래스를 '상속(extends)'하면서 여러 개의 인터페이스를 '구현(implements)'할 수 있다. 반면 파이썬은 인터페이스라는 타입의 클래스는 없기 때문에 여러 개의 클래스를 필요에 따라서 상속받을 수 있다. 그래서 인터페이스가 필수적이지 않다(interfaces are not necessary)고 한 것 같다.
그리고 파이썬에서 기본으로 제공하는 abc 모듈 말고도 zope.interface라는 외부 모듈을 이용하는 방법도 있다고 한다.
그렇다면 다시 질문으로 돌아가 보자. abc 모듈의 ABC 클래스를 상속받으면 어떻게 abc로서의 기능을 할까? 즉 ABC 클래스를 상속받으면, 어떻게 그 클래스 타입의 인스턴스를 그냥 초기화하는 것을 막을 수 있을지가 궁금했다. 답은 아마도 코드에 있을 것 같았다.
ABC 클래스의 코드는 다음과 같았다.
# abc.py
class ABC(metaclass=ABCMeta):
"""Helper class that provides a standard way to create an ABC using
inheritance.
"""
__slots__ = ()
Helper class(역시 정확한 뜻은 모른다)로써, 이 클래스를 상속받으면 ABC로써 기능할 수 있게 해 주는 클래스라고 이해했다. 코드가 정말 짧았다. 추측상 대부분의 기능은 metaclass 속성으로 선언된 ABCMeta 클래스에서 담당하는 것 같았다. 두 가지의 의문이 들었다.
- ABCMeta는 뭘 하는 클래스인가
- metaclass라는 속성은 클래스의 기본 속성인가? 어떤 클래스를 상속받는 것과 metaclass로 지정하는 것은 무슨 차이가 있나?
ABCMeta가 ABC 클래스에 어떻게 영향을 미치는지를 알기 위해서는 2번을 먼저 알아야 하겠다. 메타클래스(meta class)는 뭘까? 관련 문서도 참고해보고 GPT에게도 물어보았다. 그 결과 아주 조금은 이해하였지만 100% 이해하진 못했다.
'클래스 B가 클래스 A를 상속받는 경우'와 '클래스 B가 클래스 A를 메타클래스로 갖는 경우'의 차이는 다음과 같다. 우선 각각의 코드를 보자.
# 클래스 B가 클래스 A를 상속받는 경우
class A:
def hello(self):
return "Hello from A"
class B(A):
pass
b = B()
# 클래스 B가 클래스 A를 메타클래스로 갖는 경우
class A(type): # A는 메타클래스
def __new__(cls, name, bases, class_dict):
print(f"A 메타클래스 생성 중")
return super().__new__(cls, name, bases, class_dict)
class B(metaclass=A): # A를 메타클래스로 지정
pass
전자의 경우 B()로 인스턴스를 생성한다면 해당 인스턴스는 B의 인스턴스이자 A의 인스턴스이다. 그러나 후자의 경우는 B()로 인스턴스를 생성한다면 B 자체는 A의 인스턴스이다. 왜냐하면 B()가 실행될 때 A 메타클래스의 __new__() 메소드가 호출되기 때문이다.

차이점을 글자 그대로는 이해했는데, 아직 이해하기 쉬운 예제가 없어서 이게 왜 필요한지는 와닿지 않는다... 일단은 이 정도로 하고 다음에 더 이해해 보자.
어쨌든 돌아가서 다시 소스 코드를 보면 이 ABCMeta라는 메타클래스를 정의할 때 신기한 점이 하나 있다. 바로 abc.py 파일을 실행할 때 선택적으로 이 ABCMeta 메타클래스를 선언한다는 점이었다. 대략 이런 방식으로 코드가 짜여져 있다.
try:
from _abc import (get_cache_token, _abc_init, _abc_register,
_abc_instancecheck, _abc_subclasscheck, _get_dump,
_reset_registry, _reset_caches)
except ImportError:
from _py_abc import ABCMeta, get_cache_token
ABCMeta.__module__ = 'abc'
else:
class ABCMeta(type):
pass # 임의로 pass라고 입력함. 실제론 코드가 있다.
즉 _abc에서 import가 성공적으로 진행되어야만 ABCMeta라는 클래스가 정의된다. 그렇지 않으면 _py_abc에서 또 다른 ABCMeta 클래스를 가져와서 그 클래스의 module 값(__module__ 변수)을 'abc'로 정의한다고 이해했다.
그래서 그 밑에 ABCMeta의 코드를 봤는데, 봐도 잘 모르겠었다... 추측상 로직은 _abc 모듈의 _abc_instancecheck나 _abc_subclasscheck 등에 있을 것 같은데 확인할 수가 없었다. 일단은 여기까지만 파 보자.
'개발 일기장 > 개발 일지' 카테고리의 다른 글
파이썬으로 2시간 동안 투두 콘솔 어플리케이션 만들기 (1) | 2025.03.02 |
---|---|
20250227 TIL - Top Down Processing (0) | 2025.02.27 |
20250224 TIL (0) | 2025.02.24 |
20250221 TIL (0) | 2025.02.21 |
20250220 TIL (0) | 2025.02.20 |