Code Reading์ ์ ์์ฑ๋์ด ์๋ ํ๋ ์์ํฌ, ๋ผ์ด๋ธ๋ฌ๋ฆฌ, ํดํท ๋ฑ์ ๋ค์ํ ํ๋ก์ ํธ์ ๋ด๋ถ๋ฅผ ์ดํด๋ณด๋ ์๋ฆฌ์ฆ ์
๋๋ค. ํ๋ก์ ํธ์ ์ํคํ
์ฒ, ๋์์ธ์ฒ ํ์ด๋ ์ฝ๋ ์คํ์ผ ๋ฑ์ ์ดํด๋ณด๋ฉฐ, ๊ตฌ์ฒด์ ์ผ๋ก ํ๋ํ๋ ์ดํด๋ณด๋ ๊ฒ์ด ์๋ ์ ๋ฐ์ ์ด๋ฉด์ ๊ฐ๋จํ๊ฒ ์ดํด๋ด
๋๋ค.
Series.
์ด๋ฒ์ ๋ค๋ค๋ณด๊ณ ์ ํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๊ธฐ๋ณธ์ ์ธ ์ถ์ํ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ abc
์ ๋ํด์ ์์๋ณด๊ณ ์ ํฉ๋๋ค. Python์ ๋์ ์ธ์ด๋ก์, ๊ฐ์ฒด ์งํฅ๊ณผ ํจ์ํ ๋ฑ ๋ค์ํ ํจ๋ฌ๋ค์์ ์ง์ํ๊ณ ์์ต๋๋ค. abc
๋ชจ๋์ ์ฌ๊ธฐ์ ๊ฐ์ฒด ์งํฅ ํ๋ก๊ทธ๋๋ฐ์ ์ํ ๊ฒ์
๋๋ค. ๊ทธ๋ผ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์์ ์ดํด๋ณด๊ธฐ ์ ์ ๊ฐ๋จํ๊ฒ ๊ฐ์ฒด ์งํฅ์์์ ์ถ์ํ์ ๋ํด์ ์ง๊ณ ๋์ด๊ฒ ์ต๋๋ค.
๊ฐ์ฒด์งํฅ์์์ ์ถ์ํ
์ถ์ํ์ ๋ํด์ ยซ์ค๋ธ์ ํธยป์์๋ ์ด๋ ๊ฒ ์ด์ผ๊ธฐ๋ฅผ ํ๊ณ ์์ต๋๋ค.
์ถ์ํ๋ ์ด๋ค ์์, ์ธ๋ถ์ฌํญ, ๊ตฌ์กฐ๋ฅผ ์ข ๋ ๋ช
ํํ๊ฒ ์ดํดํ๊ธฐ ์ํด ํน์ ์ ์ฐจ๋ ๋ฌผ์ฒด๋ฅผ ์๋์ ์ผ๋ก ์๋ตํ๊ฑฐ๋ ๊ฐ์ถค์ผ๋ก์จ ๋ณต์ก๋๋ฅผ ๊ทน๋ณตํ๋ ๋ฐฉ๋ฒ์ด๋ค.[Kramer07]
- ์ถ์ํ์ ์์กดํ๋ผ ์ค์์
์ค๋ธ์ ํธ์ ์ํ์๋งค ์์คํ
์ค๊ณ ์์ (์ถ์ฒ: ์ํค๋ถ์ค)์์ ๋ค์ด์ด๊ทธ๋จ์ ๋ณด๋ฉด, ์ํ์๋ ํ ์ธ์ ์ฑ
์ด ์๊ณ ์ ์ฑ
์๋ ์กฐ๊ฑด๋ค์ด ์๋ค๋ ๊ฒ์ ์ดํดํ ์ ์์ต๋๋ค. ์ด์ ๊ฐ์ด ์ถ์ํ๋ ๊ฐ์ฒด๋ค ๊ฐ์ ํ๋ ฅ์ ํตํด์ ์ง๊ด์ ์ผ๋ก ์ดํดํ ์ ์๊ฒ ๋ฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ ์๋ก์ด ํ ์ธ ์ ์ฑ
ํน์ ์กฐ๊ฑด์ด ์ถ๊ฐ๋์ด๋ ๊ธฐ์กด์ ์ฝ๋๋ฅผ ์์ ํ์ง ์๊ณ ๊ธฐ๋ฅ์ ํ์ฅํ ์ ์๊ฒ ๋๋ ์ ์ฐํจ ๋ํ ์ถ์ํ์ ์ฅ์ ์
๋๋ค.
Abstract Base Class(ABC)
์ด๋ฌํ ์ถ์ํ์ ์ฅ์ ๋ค์ ์ฌ์ฉํ๊ธฐ ์ํด์ PEP3119 ๋ฅผ ํตํด์, Python ์ฐฝ์์์ธ ๊ท๋ ๋ฐ ๋ก์ฌ์ด ์ ์ํ์์ต๋๋ค.
PEP3119
Rationale
๋ถ๋ถ์ ๋ณด๋ฉด, ๋ค์๊ณผ ๊ฐ์ด OOP์์ ๊ฐ์ฒด์ ์ํธ์์ฉํ๋ ๋ฐฉ์์๋ 2๊ฐ์ง ์ข
๋ฅ๊ฐ ์๋ค๊ณ ์ด์ผ๊ธฐํ๊ณ ์์ต๋๋ค. ์ฒซ ๋ฒ์งธ๋ก Invocation (ํธ์ถ) ๊ทธ๋ฆฌ๊ณ Inspection (๊ฒ์ฌ) ์
๋๋ค.
ํธ์ถ์ OOP ์์ ์์, ํฉ์ฑ๋ฑ์ ๋คํ์ฑ์ ์ํด์ ํธ์ถ๋๋ ๋ฉ์๋๋ฅผ ์๋ฏธํฉ๋๋ค. Python์์๋ mro()
๋ฅผ ํตํด์ ๋ฉ์๋ ํธ์ถ ์์๋ฅผ ํ์ธํ ์๊ฐ ์์ต๋๋ค. ์ด ํธ์ถ์ ํตํด์ ์ฝ๋ ์ฌ์ฌ์ฉ์ด ๊ฐ๋ฅํด์ง๋ ๊ฒ์ด์ฃ .
class A:
pass
class B(A):
pass
class C(A):
pass
class D(B, C):
pass
>>> D.__mro__
(__main__.D, __main__.B, __main__.C, __main__.A, object)
๋ค์์ผ๋ก ๊ฒ์ฌ๋ ํด๋น ๊ฐ์ฒด๊ฐ ์ด๋ค ํด๋์ค์ ์ธ์คํด์ค์ธ์ง ํน์ ์๋ธํด๋์ค์ธ์ง, ์ธ๋ถ์์ ํธ์ถํ๋ ์์ฑ๊ฐ์ ๊ฐ์ง๊ณ ์๋์ง, ๋๋ ๋งค์๋๋ฅผ ๊ฐ์ง๊ณ ์๋์ง๋ฅผ ํ์ธํ์ฌ ํด๋น ๊ฐ์ฒด๊ฐ ์ฃผ์ด์ง ๋ฉ์์ง๋ฅผ ์ฒ๋ฆฌํ ์ ์์์ ์๋ฏธํฉ๋๋ค.
Code
abc
๋ชจ๋์ด ์ง์ํ๋ ๊ธฐ๋ฅ์ ํฌ๊ฒ 2๊ฐ์ง๊ฐ ์์ต๋๋ค.
- ๊ตฌํ์ ๊ฐ์ ํ๋
@abstractmethod
- ์์์ ๋ฐ์ ๊ฐ์ฒด๋ฅผ ์๋ธํด๋์ค๋ก ํ๋จํ๋ ๊ฒ
๊ทธ๋ ๋ค๋ฉด ์ฐจ๋ก๋๋ก 2๊ฐ์ง ๊ธฐ๋ฅ์ด ์ด๋ป๊ฒ ๊ตฌํ๋์ด ์๋์ง ์ดํด๋ณด๊ฒ ์ต๋๋ค.
@abstractmethod
# https://github.com/python/cpython/blob/3.9/Lib/abc.py#L7
def abstractmethod(funcobj):
"""A decorator indicating abstract methods.
...
Usage:
class C(metaclass=ABCMeta):
@abstractmethod
def my_abstract_method(self, ...):
...
"""
funcobj.__isabstractmethod__ = True
return funcobj
์์ ๊ฐ์ด ๋ฉ์๋์ abstract ์ฌ๋ถ๋ง ์ฒดํฌํด์ฃผ๊ณ , ์ด ํ๋๊ทธ๋ฅผ ์ด์ฉํด์ ๋์ํ๊ฒ ๋๋ ๋ก์ง๋ค์ Usage์ ์๋ ๊ฒ์ฒ๋ผ, ABCMeta
์ ๊ตฌํ์ด ๋์ด์์ต๋๋ค. ์ด์ด์ ๋ค์ ์ฝ๋๋ ํ ๋ฒ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
# https://github.com/python/cpython/blob/3.9/Lib/_py_abc.py#L14
class ABCMeta(type):
"""Metaclass for defining Abstract Base Classes (ABCs).
...
"""
_abc_invalidation_counter = 0
def __new__(mcls, name, bases, namespace, /, **kwargs):
cls = super().__new__(mcls, name, bases, namespace, **kwargs)
# Compute set of abstract method names
abstracts = {name
for name, value in namespace.items()
if getattr(value, "__isabstractmethod__", False)}
for base in bases:
for name in getattr(base, "__abstractmethods__", set()):
value = getattr(cls, name, None)
if getattr(value, "__isabstractmethod__", False):
abstracts.add(name)
cls.__abstractmethods__ = frozenset(abstracts)
...
__new__
๋ Python์ built-in function ์ค์ ํ๋๋ก์, ๊ฐ์ฒด๊ฐ ์์ฑ๋ ๋์ ํด๋น ๋ก์ง๋ค์ด ์คํ๋ฉ๋๋ค. ํด๋น ๋ถ๋ถ์ ์ฝ๋๋ฅผ ๋ณด๋ฉด __isabstractmethod__
์ฌ๋ถ๋ฅผ ์ฒดํฌํด์ abstracts
๋ฅผ ๊ตฌ์ฑํ๋ ๊ฒ์ ์ ์๊ฐ ์์ต๋๋ค. ๊ทธ๋ ์ง๋ง ๋ด๋ถ์์ ๋ฐ๋ก ์ด abstracts ์ฌ๋ถ๋ฅผ ํ์ธํ์ง๋ ์๋ ๊ฒ ๊ฐ์๋ณด์๋๋ฐ, ์ด ๋ถ๋ถ์ typeobject.c
์ฝ๋์์ ํ์ธ์ด ๊ฐ๋ฅํฉ๋๋ค.
// https://github.com/python/cpython/blob/3.9/Objects/typeobject.c#L3863
static PyObject *
object_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
...
abstract_methods = type_abstractmethods(type, NULL);
... // ์ฒดํฌ ํ ์กฐ๊ฑด์ด ์ถฉ์กฑ๋์ง ์์ผ๋ฉด
PyErr_Format(PyExc_TypeError,
"Can't instantiate abstract class %s "
"with abstract method%s %U",
type->tp_name,
method_count > 1 ? "s" : "",
joined);
# ์์์ฝ๋
from abc import ABCMeta
class A(metaclass=ABCMeta):
def __init__(self):
pass
@abstractmethod
def test(self):
pass
class B(A):
def __init__(self):
pass
>>> b = B()
TypeError: Can't instantiate abstract class B with abstract methods test
์๋ฅผ ๋ค๋ฉด, abstractmethod
์ธ test
๋ ๊ตฌํ์ ํด์ผํ๋๋ฐ, B ํด๋์ค ์์์ ๊ตฌํ์ด ๋์ด์์ง ์๊ธฐ ๋๋ฌธ์ ์๋ฌ๋ฉ์์ง๊ฐ ๋จ๊ฒ ๋๋ ๊ฒ์ด์ฃ .
subclass
์ถ์ ํด๋์ค๋ฅผ ์์๋ฐ์์ ๊ตฌํํ๊ฒ ๋๋ ๊ฒฝ์ฐ, ์ด ํด๋์ค๋ ์ถ์ ํด๋์ค์ ์๋ธํด๋์ค๊ฐ ๋ฉ๋๋ค.
์๋ธํด๋์ค ํ๋จ ๋ํ ๊ฐ๋จํ๊ฒ ๋์ด์์ต๋๋ค. ๋ค์ ABCMeta
์ __new__
๋ถ๋ถ์ผ๋ก ๋์๊ฐ๋ณด๊ฒ ์ต๋๋ค.
# https://github.com/python/cpython/blob/3.9/Lib/_py_abc.py#L48
_abc_invalidation_counter = 0
def __new__(...):
...
# Set up inheritance registry
cls._abc_registry = WeakSet()
cls._abc_cache = WeakSet()
cls._abc_negative_cache = WeakSet()
cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter
return cls
cls ์ฆ, ํด๋์ค์ ์์ฑ์ผ๋ก ์ฌ์ฉ๋๊ณ ์๋ ๊ฐ๋ค์ ๋ชจ๋ ์๋ธํด๋์ค๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ํจ์
๋๋ค. ๊ฐ๋จํ๊ฒ ์ญํ ์ ์ดํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
cls._abc_registry
: register
๋ฉ์๋๋ฅผ ํตํด์ ์๋ธํด๋์ค๋ฅผ ๋ฑ๋ก
cls._abc_cache
: ์๋ธํด๋์ค์ผ ๊ฒฝ์ฐ ์บ์ฑํ๋ ์ฉ๋
cls._abc_negative_cache
: ์๋ธํด๋์ค๊ฐ ์๋ ๊ฒฝ์ฐ๋ฅผ ์บ์ฑํ๋ ์ฉ๋
์ถ๊ฐ์ ์ผ๋ก ์ดํด๋ณผ ๊ฒ์ ์๋ฃ๊ตฌ์กฐ๋ก ์ฌ์ฉํ๊ณ ์๋ WeekSet
์
๋๋ค. ์ฝํ ์ฐธ์กฐ๋ฅผ ์ง์ํ๋ weekref
๋ชจ๋์ ํตํด์ ๊ตฌ์ฑ๋ Set ์๋ฃ๊ตฌ์กฐ์
๋๋ค. ์ฝํ ์ฐธ์กฐ๋ ๋ฌด์์ด๊ณ , ์ฌ๊ธฐ์ ์ฝํ ์ฐธ์กฐ๊ฐ ์ ์ฌ์ฉ๋์์๊น์?
ํํ ๊ฐํ ์ฐธ์กฐ (Strong Reference)์ ์ฝํ ์ฐธ์กฐ (Weak Reference)๋ก ๋๋์ด์ ๋ณผ ์๊ฐ ์๋๋ฐ, ์ด๋ GC(Garbage Collection)์ ์ํด์ ์ฌ๋ผ์ง๋๋ ๋ง๋๋์ ์ฐจ์ด๋ก ๋ณผ ์ ์์ต๋๋ค. ๊ฐํ ์ฐธ์กฐ์ ๊ฒฝ์ฐ๋ ์ฐธ์กฐ์(reference count)๊ฐ 0์ด ๋๊ฑฐ๋ ๋ฉ๋ชจ๋ฆฌ์์ ํด์ ๋ ๋ ์ ๊ฑฐ๋๊ณ , ์ฝํ ์ฐธ์กฐ์ ๊ฒฝ์ฐ์๋ ์ฐธ์กฐ์๋ฅผ ์ฌ๋ฆฌ์ง ์๊ธฐ ๋๋ฌธ์ GC์ ์ํด์ ๋งค๋ฒ ์ ๊ฑฐ๊ฐ ๋ฉ๋๋ค.
์ด๋ ๊ฒ ์ฝ๊ฒ ์ ๊ฑฐ๋ ์ ์๋ ์ฑ์ง์ Cache์ ์ฌ์ฉ์ด ๋ฉ๋๋ค. Cache๋ ์์ฃผ ๋ฑ์ฅํ๋ ๊ฒฝ์ฐ๋ฅผ ๋น ๋ฅด๊ฒ ์ฒ๋ฆฌํ ์ ์๊ณ , ์ ๊ฑฐ๊ฐ ๋๋๋ผ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ง ์๊ธฐ ๋๋ฌธ์
๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋ฉ๋ชจ๋ฆฌ ๋์๋ฅผ ๋ฏธ๋ฆฌ ๋ฐฉ์งํ๊ธฐ ์ํด์ ์ฌ์ฉํ๊ณ ์๋ค๊ณ ๋ณผ ์ ์์ต๋๋ค.
์ถ๊ฐ์ ์ผ๋ก negative cache
์ ๊ฒฝ์ฐ, DNS์์ ์กด์ฌํ์ง ์๋ ํธ์คํธ์ ๋ํ ์์ฒญ์ ์บ์ฑํ์ฌ ๋น ๋ฅด๊ฒ ๋ต์ ํด์ฃผ๋ ์ฉ๋๋ก ์ฌ์ฉ๋๋ ๋ฐฉ์์
๋๋ค. ์๋ธํด๋์ค ์ฒดํฌ ๋ํ ๋ค์ํ ํด๋์ค์ ๋น๊ต๋ฅผ ํ๊ฒ ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์, negative cache๊น์ง ๊ตฌํ๋์ด ์๋ ๊ฒ์ผ๋ก ์ดํด๊ฐ ๋ฉ๋๋ค. ๋น์ทํ ์ํฉ์์ ์จ๋จน์ ์ ์์ ๊ฒ ๊ฐ๋ค์.
# https://github.com/python/cpython/blob/3.9/Lib/_py_abc.py#L54
def register(cls, subclass):
"""Register a virtual subclass of an ABC."""
# https://github.com/python/cpython/blob/3.9/Lib/_py_abc.py#L108
def __subclasscheck__(cls, subclass):
"""Override for issubclass(subclass, cls)."""
๋ง์ง๋ง์ผ๋ก ์๋ธํด๋์ค ๊ด๋ จํด์๋ ์ฃผ์ ๋ฉ์๋์ธ ๋ฑ๋ก(register
)๊ณผ ์ฒดํฌ์ ๋(__subclasscheck__
)๊ฐ ์์ต๋๋ค. ๋ด๋ถ์ ์ฝ๋๋ ๋จ์ํ ํธ์ด๋ผ์ ๊ตณ์ด ๋ค๋ฃจ์ง ์์๋ ๊ด์ฐฎ์ ๊ฒ ๊ฐ์ต๋๋ค.
collections.abc
ํํ ์์ฃผ ์ฌ์ฉ๋๋ collections
์์๋ ์ด abc
์ด ์ ์ฉ๋์ด ์์ต๋๋ค. ๋ฐ๋ก collections.abc
์
๋๋ค. ์ฌ๊ธฐ์๋ ๊ธฐ๋ณธ์ ์ธ ์๋ฃ๊ตฌ์กฐ๋ค์ ๋ํ ์ถ์ ํด๋์ค๋ฅผ ์ ๊ณตํ๊ณ ์์ต๋๋ค. ๋ฌธ์์ ์๋ ๋ฆฌ์คํธ๋ฅผ ํ๋ฒ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
collection.abc์ ๋ค์ด์ด๊ทธ๋จ (์ถ์ฒ: Issue Tracker – Python.org)์ ํด๋์ค๋ฅผ ์์ํด์ ์ฌ์ฉํ๋ค๋ฉด, ํด๋น ํด๋์ค์ ๋ง์ถฐ์ ๋ฉ์๋๋ค์ ๊ตฌํํด์ผ ํฉ๋๋ค. ๊ต์ฅํ ์์ฃผ ์ฐ์ด๋, ๊ธฐ๋ณธ์ ์ธ ์๋ฃ๊ตฌ์กฐํ์ ์ถ์ ๋ฒ ์ด์ค ํด๋์ค๋ค์ด๋ฏ๋ก, ์ง๊ด์ ์ผ๋ก ์ฌ์ฉํ ์ ์์ ๊ฒ์ด๋ผ๊ณ ๊ธฐ๋๊ฐ ๋ฉ๋๋ค.
์ฌ๊ธฐ์ ๋ํ ์ฝ๋๋ ์ฌ๊ธฐ์์ ํ์ธ์ด ๊ฐ๋ฅํฉ๋๋ค. ๊ฐ๋จํ ์ ๋ฆฌํ๋ฉด Metaclass๊ฐ ๋ฏน์ค์ธ์ผ๋ก ์ฌ์ฉ๋๊ณ , ์๋ธํด๋์ค ์ฒดํฌ์ฉ์ผ๋ก __subclasshook__
์์ ๋ฉ์๋๋ค์ ํ์ธํ๋๋ก ๋์ด์์ต๋๋ค.
๋์ผ๋ก
Python์์ ์ง์ํ๋ ์ถ์ํ ๋ชจ๋์ธ abc
์ ๋ํด์ ์ฝ๋์ ํจ๊ป ์์๋ณด์์ต๋๋ค. ์กฐ๊ธ ๋ Pythonic ํ ์ฝ๋๋ฅผ ๊ตฌํํ๊ณ ์ถ๋ค๋ฉด, ์์ collections.abc ์ ๊ธฐ๋ณธ ์ถ์ํ ํด๋์ค๋ฅผ ์์๋ฐ์ ํ์ํ ๊ธฐ๋ฅ๋ค์ ๊ตฌํํด์ ์ฌ์ฉํด๋ณด๋ ๊ฒ์ด ์ด๋จ๊น์?
References