Python Descriptor Protocol์ ๋ณต์ก์ฑ์ ํ๊ตฌํ๊ณ , ์ฑ๋ฅ์ ๋ฏธ์น๋ ์ํฅ๊ณผ ์ ์ญ Python ํ๋ก์ ํธ์์ ํจ์จ์ ์ธ ๊ฐ์ฒด ์์ฑ ์ ๊ทผ์ ํ์ฉํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์ธ์.
์ฑ๋ฅ ๊ทน๋ํ: ๊ฐ์ฒด ์์ฑ ์ ๊ทผ์ ์ํ Python Descriptor Protocol ์ฌ์ธต ๋ถ์
์ํํธ์จ์ด ๊ฐ๋ฐ์ ์ญ๋์ ์ธ ํ๊ฒฝ์์ ํจ์จ์ฑ๊ณผ ์ฑ๋ฅ์ ๋งค์ฐ ์ค์ํฉ๋๋ค. Python ๊ฐ๋ฐ์์๊ฒ ๊ฐ์ฒด ์์ฑ ์ ๊ทผ์ ์ ์ดํ๋ ํต์ฌ ๋ฉ์ปค๋์ฆ์ ์ดํดํ๋ ๊ฒ์ ํ์ฅ ๊ฐ๋ฅํ๊ณ ๊ฒฌ๊ณ ํ๋ฉฐ ๊ณ ์ฑ๋ฅ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๋ ๋ฐ ๋งค์ฐ ์ค์ํฉ๋๋ค. ์ด ์ค์ฌ์๋ Python์ ๊ฐ๋ ฅํ์ง๋ง ์ข ์ข ํ์ฉ๋๊ฐ ๋ฎ์ Descriptor Protocol์ด ์์ต๋๋ค. ์ด ๊ธฐ์ฌ๋ ์ด ํ๋กํ ์ฝ์ ๋ํ ํฌ๊ด์ ์ธ ํ๊ตฌ๋ฅผ ์์ํ์ฌ ๊ทธ ๋ฉ์ปค๋์ฆ์ ๋ถ์ํ๊ณ , ์ฑ๋ฅ์ ๋ฏธ์น๋ ์ํฅ์ ์กฐ๋ช ํ๋ฉฐ, ๋ค์ํ ๊ธ๋ก๋ฒ ๊ฐ๋ฐ ์๋๋ฆฌ์ค์์ ์ ์ฉํ ์ ์๋ ์ค์ง์ ์ธ ํต์ฐฐ๋ ฅ์ ์ ๊ณตํฉ๋๋ค.
Descriptor Protocol์ด๋ ๋ฌด์์ ๋๊น?
ํต์ฌ์ ์ผ๋ก Python์ Descriptor Protocol์ ๊ฐ์ฒด๊ฐ ์์ฑ ์ ๊ทผ(๊ฐ์ ธ์ค๊ธฐ, ์ค์ ๋ฐ ์ญ์ )์ ์ฒ๋ฆฌํ๋ ๋ฐฉ์์ ์ฌ์ฉ์ ์ ์ํ ์ ์๋๋ก ํ๋ ๋ฉ์ปค๋์ฆ์
๋๋ค. ๊ฐ์ฒด๊ฐ ํน์ ๋ฉ์๋ __get__, __set__ ๋๋ __delete__ ์ค ํ๋ ์ด์์ ๊ตฌํํ๋ฉด descriptor๊ฐ ๋ฉ๋๋ค. ์ด๋ฌํ ๋ฉ์๋๋ ํด๋น descriptor๋ฅผ ๊ฐ์ง ํด๋์ค์ ์ธ์คํด์ค์์ ์์ฑ ์กฐํ, ํ ๋น ๋๋ ์ญ์ ๊ฐ ๋ฐ์ํ ๋ ํธ์ถ๋ฉ๋๋ค.
ํต์ฌ ๋ฉ์๋: `__get__`, `__set__`, ๋ฐ `__delete__`
__get__(self, instance, owner): ์ด ๋ฉ์๋๋ ์์ฑ์ ์ ๊ทผํ ๋ ํธ์ถ๋ฉ๋๋ค.self: descriptor ์ธ์คํด์ค ์์ฒด.instance: ์์ฑ์ด ์ ๊ทผ๋ ํด๋์ค์ ์ธ์คํด์ค. ์์ฑ์ด ํด๋์ค ์์ฒด์์ ์ ๊ทผ๋๋ ๊ฒฝ์ฐ(์:MyClass.my_attribute),instance๋None์ด ๋ฉ๋๋ค.owner: descriptor๋ฅผ ์์ ํ ํด๋์ค.__set__(self, instance, value): ์ด ๋ฉ์๋๋ ์์ฑ์ ๊ฐ์ด ํ ๋น๋ ๋ ํธ์ถ๋ฉ๋๋ค.self: descriptor ์ธ์คํด์ค.instance: ์์ฑ์ด ์ค์ ๋๋ ํด๋์ค์ ์ธ์คํด์ค.value: ์์ฑ์ ํ ๋น๋๋ ๊ฐ.__delete__(self, instance): ์ด ๋ฉ์๋๋ ์์ฑ์ด ์ญ์ ๋ ๋ ํธ์ถ๋ฉ๋๋ค.self: descriptor ์ธ์คํด์ค.instance: ์์ฑ์ด ์ญ์ ๋๋ ํด๋์ค์ ์ธ์คํด์ค.
Descriptor ์๋ ๋ฐฉ์
์ธ์คํด์ค์์ ์์ฑ์ ์ ๊ทผํ๋ฉด Python์ ์์ฑ ์กฐํ ๋ฉ์ปค๋์ฆ์ ์๋นํ ์ ๊ตํฉ๋๋ค. ๋จผ์ ์ธ์คํด์ค์ ๋์
๋๋ฆฌ๋ฅผ ํ์ธํฉ๋๋ค. ์์ฑ์ด ๊ฑฐ๊ธฐ์ ์์ผ๋ฉด ํด๋์ค์ ๋์
๋๋ฆฌ๋ฅผ ๊ฒ์ฌํฉ๋๋ค. descriptor(__get__, __set__ ๋๋ __delete__๊ฐ ์๋ ๊ฐ์ฒด)๊ฐ ํด๋์ค์ ๋์
๋๋ฆฌ์ ์๋ ๊ฒฝ์ฐ Python์ ์ ์ ํ descriptor ๋ฉ์๋๋ฅผ ํธ์ถํฉ๋๋ค. ํต์ฌ์ descriptor๊ฐ ํด๋์ค ์์ค์์ ์ ์๋์ง๋ง ํด๋น ๋ฉ์๋๋ *์ธ์คํด์ค ์์ค*(๋๋ instance๊ฐ None์ธ ๊ฒฝ์ฐ __get__์ ๋ํ ํด๋์ค ์์ค)์์ ์๋ํ๋ค๋ ๊ฒ์
๋๋ค.
์ฑ๋ฅ ์ธก๋ฉด: Descriptor๊ฐ ์ค์ํ ์ด์
Descriptor๋ ๊ฐ๋ ฅํ ์ฌ์ฉ์ ์ ์ ๊ธฐ๋ฅ์ ์ ๊ณตํ์ง๋ง ์ฑ๋ฅ์ ๋ฏธ์น๋ ์ฃผ์ ์ํฅ์ ์์ฑ ์ ๊ทผ์ ๊ด๋ฆฌํ๋ ๋ฐฉ์์์ ๋น๋กฏ๋ฉ๋๋ค. ์์ฑ ์์ ์ ๊ฐ๋ก์ฑ์ descriptor๋ ๋ค์์ ์ํํ ์ ์์ต๋๋ค.
- ๋ฐ์ดํฐ ์ ์ฅ ๋ฐ ๊ฒ์ ์ต์ ํ: Descriptor๋ ๋ฐ์ดํฐ๋ฅผ ํจ์จ์ ์ผ๋ก ์ ์ฅํ๊ณ ๊ฒ์ํ๋ ๋ก์ง์ ๊ตฌํํ์ฌ ์ค๋ณต ๊ณ์ฐ์ด๋ ๋ณต์กํ ์กฐํ๋ฅผ ํผํ ์ ์์ต๋๋ค.
- ์ ์ฝ ์กฐ๊ฑด ๋ฐ ์ ํจ์ฑ ๊ฒ์ฌ ์ ์ฉ: descriptor๋ ์์ฑ ์ค์ ์ค์ ์ ํ ๊ฒ์ฌ, ๋ฒ์ ์ ํจ์ฑ ๊ฒ์ฌ ๋๋ ๊ธฐํ ๋น์ฆ๋์ค ๋ก์ง์ ์ํํ์ฌ ์๋ชป๋ ๋ฐ์ดํฐ๊ฐ ์์คํ ์ ์ผ์ฐ ๋ค์ด๊ฐ๋ ๊ฒ์ ๋ฐฉ์งํ ์ ์์ต๋๋ค. ์ด๋ฅผ ํตํด ์ ํ๋ฆฌ์ผ์ด์ ๋ผ์ดํ์ฌ์ดํด ํ๋ฐ์ ์ฑ๋ฅ ๋ณ๋ชฉ ํ์์ ๋ฐฉ์งํ ์ ์์ต๋๋ค.
- ์ง์ฐ ๋ก๋ฉ ๊ด๋ฆฌ: Descriptor๋ ์ค์ ๋ก ํ์ํ ๋๊น์ง ๊ณ ๋น์ฉ ๋ฆฌ์์ค์ ์์ฑ ๋๋ ๊ฐ์ ธ์ค๊ธฐ๋ฅผ ์ฐ๊ธฐํ์ฌ ์ด๊ธฐ ๋ก๋ ์๊ฐ์ ๊ฐ์ ํ๊ณ ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ์ ์ค์ผ ์ ์์ต๋๋ค.
- ์์ฑ ๊ฐ์์ฑ ๋ฐ ๋ณ๊ฒฝ ๊ฐ๋ฅ์ฑ ์ ์ด: descriptor๋ ๋ค์ํ ์กฐ๊ฑด์ ๋ฐ๋ผ ์์ฑ์ ์ ๊ทผํ๊ฑฐ๋ ์์ ํ ์ ์๋์ง ๋์ ์ผ๋ก ๊ฒฐ์ ํ ์ ์์ต๋๋ค.
- ์บ์ฑ ๋ฉ์ปค๋์ฆ ๊ตฌํ: ๋ฐ๋ณต์ ์ธ ๊ณ์ฐ ๋๋ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ๋ฅผ descriptor ๋ด์์ ์บ์ํ ์ ์์ด ์๋๊ฐ ํฌ๊ฒ ํฅ์๋ฉ๋๋ค.
Descriptor์ ์ค๋ฒํค๋
descriptor ์ฌ์ฉ๊ณผ ๊ด๋ จ๋ ์์ ์ค๋ฒํค๋๊ฐ ์๋ค๋ ์ ์ ์ธ์ ํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. descriptor์ ๊ด๋ จ๋ ๊ฐ ์์ฑ ์ ๊ทผ, ํ ๋น ๋๋ ์ญ์ ๋ ๋ฉ์๋ ํธ์ถ์ ๋ฐ์์ํต๋๋ค. ์์ฃผ ์ ๊ทผ๋๊ณ ํน๋ณํ ๋ก์ง์ด ํ์ํ์ง ์์ ๋งค์ฐ ๊ฐ๋จํ ์์ฑ์ ๊ฒฝ์ฐ ์ง์ ์ ๊ทผํ๋ ๊ฒ์ด ์ฝ๊ฐ ๋ ๋น ๋ฅผ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ์ด ์ค๋ฒํค๋๋ ์ผ๋ฐ์ ์ธ ์ ํ๋ฆฌ์ผ์ด์ ์ฑ๋ฅ์ ํฐ ํ์์ ์ข ์ข ๋ฌด์ํ ์ ์์ผ๋ฉฐ ์ ์ฐ์ฑ๊ณผ ์ ์ง ๊ด๋ฆฌ์ฑ ํฅ์์ด๋ผ๋ ์ด์ ์ ๋๋ฆด ๊ฐ์น๊ฐ ์์ต๋๋ค.
์ค์ํ ์ ์ descriptor๊ฐ ๋ณธ์ง์ ์ผ๋ก ๋๋ฆฐ ๊ฒ์ด ์๋๋ผ ํด๋น __get__, __set__ ๋ฐ __delete__ ๋ฉ์๋ ๋ด์ ๊ตฌํ๋ ๋ก์ง์ ์ง์ ์ ์ธ ๊ฒฐ๊ณผ๋ผ๋ ๊ฒ์
๋๋ค. ์ ์ค๊ณ๋ descriptor ๋ก์ง์ ์ฑ๋ฅ์ ํฌ๊ฒ ํฅ์์ํฌ ์ ์์ต๋๋ค.
์ผ๋ฐ์ ์ธ ์ฌ์ฉ ์ฌ๋ก ๋ฐ ์ค์ ์์
Python์ ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ง์ ์ธ๊ธฐ ์๋ ํ๋ ์์ํฌ๋ descriptor๋ฅผ ๊ด๋ฒ์ํ๊ฒ ์ฌ์ฉํ๋ฉฐ ์ข ์ข ์๋ฌต์ ์ผ๋ก ์ฌ์ฉํฉ๋๋ค. ์ด๋ฌํ ํจํด์ ์ดํดํ๋ฉด ๋์์ ๋ ์ ์ดํดํ๊ณ ์์ ๋ง์ ๊ตฌํ์ ์๊ฐ์ ์ค ์ ์์ต๋๋ค.
1. Properties (`@property`)
descriptor์ ๊ฐ์ฅ ์ผ๋ฐ์ ์ธ ํํ์ @property ๋ฐ์ฝ๋ ์ดํฐ์
๋๋ค. @property๋ฅผ ์ฌ์ฉํ๋ฉด Python์ ๋ฐฑ๊ทธ๋ผ์ด๋์์ descriptor ๊ฐ์ฒด๋ฅผ ์๋์ผ๋ก ๋ง๋ญ๋๋ค. ์ด๋ฅผ ํตํด ๊ธฐ๋ณธ ๊ตฌํ ์ธ๋ถ ์ ๋ณด๋ฅผ ๋
ธ์ถํ์ง ์๊ณ ๋ getter, setter ๋ฐ deleter ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ์์ฑ์ฒ๋ผ ๋์ํ๋ ๋ฉ์๋๋ฅผ ์ ์ํ ์ ์์ต๋๋ค.
class User:
def __init__(self, name, email):
self._name = name
self._email = email
@property
def name(self):
print("Getting name...")
return self._name
@name.setter
def name(self, value):
print(f"Setting name to {value}...")
if not isinstance(value, str) or not value:
raise ValueError("Name must be a non-empty string")
self._name = value
@property
def email(self):
return self._email
# Usage
user = User("Alice", "alice@example.com")
print(user.name) # Calls the getter
user.name = "Bob" # Calls the setter
# user.email = "new@example.com" # This would raise an AttributeError as there's no setter
๊ธ๋ก๋ฒ ๊ด์ : ๊ตญ์ ์ฌ์ฉ์ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃจ๋ ์ ํ๋ฆฌ์ผ์ด์ ์์ properties๋ฅผ ์ฌ์ฉํ์ฌ ๋ค์ํ ์ง์ญ ํ์ค์ ๋ฐ๋ผ ์ด๋ฆ์ด๋ ์ด๋ฉ์ผ ์ฃผ์๋ฅผ ๊ฒ์ฆํ๊ณ ํฌ๋งทํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, setter๋ ์ด๋ฆ์ด ๋ค๋ฅธ ์ธ์ด์ ๋ํ ํน์ ๋ฌธ์ ์งํฉ ์๊ตฌ ์ฌํญ์ ์ค์ํ๋์ง ํ์ธํ ์ ์์ต๋๋ค.
2. `classmethod` ๋ฐ `staticmethod`
@classmethod์ @staticmethod๋ ๋ชจ๋ descriptor๋ฅผ ์ฌ์ฉํ์ฌ ๊ตฌํ๋ฉ๋๋ค. ๊ฐ๊ฐ ํด๋์ค ์์ฒด์์ ์๋ํ๊ฑฐ๋ ์ธ์คํด์ค์ ๋
๋ฆฝ์ ์ผ๋ก ์๋ํ๋ ๋ฉ์๋๋ฅผ ์ ์ํ๋ ํธ๋ฆฌํ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค.
class ConfigurationManager:
_instance = None
def __init__(self):
self.settings = {}
@classmethod
def get_instance(cls):
if cls._instance is None:
cls._instance = cls()
return cls._instance
@staticmethod
def validate_setting(key, value):
# Basic validation logic
if not isinstance(key, str) or not key:
return False
return True
# Usage
config = ConfigurationManager.get_instance() # Calls classmethod
print(ConfigurationManager.validate_setting("timeout", 60)) # Calls staticmethod
๊ธ๋ก๋ฒ ๊ด์ : get_instance์ ๊ฐ์ classmethod๋ ์ง์ญ๋ณ ๊ธฐ๋ณธ๊ฐ(์: ๊ธฐ๋ณธ ํตํ ๊ธฐํธ, ๋ ์ง ํ์)์ ํฌํจํ ์ ์๋ ์ ํ๋ฆฌ์ผ์ด์
์ ์ฒด ๊ตฌ์ฑ ๊ด๋ฆฌํ๋ ๋ฐ ์ฌ์ฉ๋ ์ ์์ต๋๋ค. staticmethod๋ ๋ค์ํ ์ง์ญ์ ๊ฑธ์ณ ๋ณดํธ์ ์ผ๋ก ์ ์ฉ๋๋ ์ผ๋ฐ์ ์ธ ์ ํจ์ฑ ๊ฒ์ฌ ๊ท์น์ ์บก์ํํ ์ ์์ต๋๋ค.
3. ORM ํ๋ ์ ์
SQLAlchemy ๋ฐ Django์ ORM๊ณผ ๊ฐ์ ๊ฐ์ฒด-๊ด๊ณ ๋งคํผ(ORM)๋ ๋ชจ๋ธ ํ๋๋ฅผ ์ ์ํ๊ธฐ ์ํด descriptor๋ฅผ ๊ด๋ฒ์ํ๊ฒ ํ์ฉํฉ๋๋ค. ๋ชจ๋ธ ์ธ์คํด์ค์์ ํ๋์ ์ ๊ทผํ๋ฉด(์: user.username) ORM์ descriptor๋ ์ด ์ ๊ทผ์ ๊ฐ๋ก์ฑ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ฑฐ๋ ์ ์ฅํ ๋ฐ์ดํฐ๋ฅผ ์ค๋นํฉ๋๋ค. ์ด ์ถ์ํ๋ฅผ ํตํด ๊ฐ๋ฐ์๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ ์ฝ๋๋ฅผ ์ผ๋ฐ Python ๊ฐ์ฒด์ฒ๋ผ ์กฐ์ํ ์ ์์ต๋๋ค.
# Simplified example inspired by ORM concepts
class AttributeDescriptor:
def __init__(self, column_name):
self.column_name = column_name
self.storage = {}
def __get__(self, instance, owner):
if instance is None:
return self # Accessing on class
return self.storage.get(self.column_name)
def __set__(self, instance, value):
self.storage[self.column_name] = value
class User:
username = AttributeDescriptor("username")
email = AttributeDescriptor("email")
def __init__(self, username, email):
self.username = username
self.email = email
# Usage
user1 = User("global_user_1", "global1@example.com")
print(user1.username) # Accesses __get__ on AttributeDescriptor
user1.username = "updated_user"
print(user1.username)
# Note: In a real ORM, storage would interact with a database.
๊ธ๋ก๋ฒ ๊ด์ : ORM์ ๋ฐ์ดํฐ๊ฐ ๋ค๋ฅธ ๋ก์ผ์ผ์์ ๊ด๋ฆฌ๋์ด์ผ ํ๋ ๊ธ๋ก๋ฒ ์ ํ๋ฆฌ์ผ์ด์
์์ ๊ธฐ๋ณธ์ ์
๋๋ค. descriptor๋ ์ผ๋ณธ์ ์ฌ์ฉ์๊ฐ user.address์ ์ ๊ทผํ ๋ ์ฌ๋ฐ๋ฅธ ์ง์ญํ๋ ์ฃผ์ ํ์์ด ๊ฒ์๋๊ณ ํ์๋๋๋ก ๋ณด์ฅํ๋ฉฐ descriptor์ ์ํด ์ค์ผ์คํธ๋ ์ด์
๋ ๋ณต์กํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ๋ฅผ ํฌํจํ ์ ์์ต๋๋ค.
4. ์ฌ์ฉ์ ์ ์ ๋ฐ์ดํฐ ์ ํจ์ฑ ๊ฒ์ฌ ๋ฐ ์ง๋ ฌํ ๊ตฌํ
์ฌ์ฉ์ ์ ์ descriptor๋ฅผ ๋ง๋ค์ด ๋ณต์กํ ์ ํจ์ฑ ๊ฒ์ฌ ๋๋ ์ง๋ ฌํ ๋ก์ง์ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ๊ธ์ต ๊ธ์ก์ด ํญ์ ๊ธฐ๋ณธ ํตํ๋ก ์ ์ฅ๋๊ณ ๊ฒ์ ์ ํ์ง ํตํ๋ก ๋ณํ๋๋๋ก ํฉ๋๋ค.
class CurrencyField:
def __init__(self, currency_code='USD'):
self.currency_code = currency_code
self._data = {}
def __get__(self, instance, owner):
if instance is None:
return self
amount = self._data.get('amount', 0)
# In a real scenario, exchange rates would be fetched dynamically
exchange_rate = {'USD': 1.0, 'EUR': 0.92, 'JPY': 150.5}
return amount * exchange_rate.get(self.currency_code, 1.0)
def __set__(self, instance, value):
# Assume value is always in USD for simplicity
if not isinstance(value, (int, float)) or value < 0:
raise ValueError("Amount must be a non-negative number.")
self._data['amount'] = value
class Product:
price = CurrencyField()
eur_price = CurrencyField(currency_code='EUR')
jpy_price = CurrencyField(currency_code='JPY')
def __init__(self, price_usd):
self.price = price_usd # Sets the base USD price
# Usage
product = Product(100) # Initial price is $100
print(f"Price in USD: {product.price:.2f}")
print(f"Price in EUR: {product.eur_price:.2f}")
print(f"Price in JPY: {product.jpy_price:.2f}")
product.price = 200 # Update base price
print(f"Updated Price in EUR: {product.eur_price:.2f}")
๊ธ๋ก๋ฒ ๊ด์ : ์ด ์์ ๋ ๋ค์ํ ํตํ๋ฅผ ์ฒ๋ฆฌํด์ผ ํ๋ ํ์์ฑ์ ์ง์ ์ ์ผ๋ก ํด๊ฒฐํฉ๋๋ค. ๊ธ๋ก๋ฒ ์ ์ ์๊ฑฐ๋ ํ๋ซํผ์ ์ ์ฌํ ๋ก์ง์ ์ฌ์ฉํ์ฌ ๋ค๋ฅธ ๊ตญ๊ฐ์ ์ฌ์ฉ์์ ๋ํด ๊ฐ๊ฒฉ์ ์ฌ๋ฐ๋ฅด๊ฒ ํ์ํ๊ณ ํ์ฌ ํ์จ์ ๊ธฐ๋ฐ์ผ๋ก ํตํ๋ฅผ ์๋์ผ๋ก ๋ณํํฉ๋๋ค.
๊ณ ๊ธ Descriptor ๊ฐ๋ ๋ฐ ์ฑ๋ฅ ๊ณ ๋ ค ์ฌํญ
๊ธฐ๋ณธ ์ฌํญ ์ธ์๋ descriptor๊ฐ ๋ค๋ฅธ Python ๊ธฐ๋ฅ๊ณผ ์ํธ ์์ฉํ๋ ๋ฐฉ์์ ์ดํดํ๋ฉด ๋์ฑ ์ ๊ตํ ํจํด๊ณผ ์ฑ๋ฅ ์ต์ ํ๋ฅผ ํ์ฉํ ์ ์์ต๋๋ค.
1. ๋ฐ์ดํฐ vs. ๋น๋ฐ์ดํฐ Descriptor
Descriptor๋ __set__ ๋๋ __delete__๋ฅผ ๊ตฌํํ๋์ง ์ฌ๋ถ์ ๋ฐ๋ผ ๋ถ๋ฅ๋ฉ๋๋ค.
- ๋ฐ์ดํฐ Descriptor:
__get__๊ณผ__set__๋๋__delete__์ค ํ๋ ์ด์์ ๊ตฌํํฉ๋๋ค. - ๋น๋ฐ์ดํฐ Descriptor:
__get__๋ง ๊ตฌํํฉ๋๋ค.
์ด ๊ตฌ๋ถ์ ์์ฑ ์กฐํ ์ฐ์ ์์์ ์ค์ํฉ๋๋ค. Python์ด ์์ฑ์ ์กฐํํ ๋ ์ธ์คํด์ค์ ๋์ ๋๋ฆฌ์์ ์ฐพ์ ์์ฑ๋ณด๋ค ํด๋์ค์ ์ ์๋ ๋ฐ์ดํฐ descriptor๋ฅผ ์ฐ์ ์ํฉ๋๋ค. ๋น๋ฐ์ดํฐ descriptor๋ ์ธ์คํด์ค ์์ฑ ๋ค์์ ๊ณ ๋ ค๋ฉ๋๋ค.
์ฑ๋ฅ ์ํฅ: ์ด ์ฐ์ ์์๋ ๋ฐ์ดํฐ descriptor๊ฐ ์ธ์คํด์ค ์์ฑ์ ํจ๊ณผ์ ์ผ๋ก ์ฌ์ ์ํ ์ ์์์ ์๋ฏธํฉ๋๋ค. ์ด๋ properties ๋ฐ ORM ํ๋๊ฐ ์๋ํ๋ ๋ฐฉ์์ ๊ธฐ๋ณธ์
๋๋ค. ํด๋์ค์ 'name'์ด๋ผ๋ ๋ฐ์ดํฐ descriptor๊ฐ ์๋ ๊ฒฝ์ฐ instance.name์ ์ ๊ทผํ๋ฉด 'name'์ด ์ธ์คํด์ค์ __dict__์ ์๋์ง ์ฌ๋ถ์ ๊ด๊ณ์์ด ํญ์ descriptor์ __get__ ๋ฉ์๋๊ฐ ํธ์ถ๋ฉ๋๋ค. ์ด๋ฅผ ํตํด ์ผ๊ด๋ ๋์์ ๋ณด์ฅํ๊ณ ์ ์ด๋ ์ ๊ทผ์ ํ์ฉํฉ๋๋ค.
2. Descriptor ๋ฐ `__slots__`
__slots__๋ฅผ ์ฌ์ฉํ๋ฉด ์ธ์คํด์ค ๋์
๋๋ฆฌ ์์ฑ์ ๋ฐฉ์งํ์ฌ ๋ฉ๋ชจ๋ฆฌ ์๋น๋ฅผ ํฌ๊ฒ ์ค์ผ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ descriptor๋ ํน์ ๋ฐฉ์์ผ๋ก __slots__์ ์ํธ ์์ฉํฉ๋๋ค. descriptor๊ฐ ํด๋์ค ์์ค์์ ์ ์๋ ๊ฒฝ์ฐ ์์ฑ ์ด๋ฆ์ด __slots__์ ๋์ด๋์ด ์์ด๋ ํธ์ถ๋ฉ๋๋ค. descriptor๊ฐ ์ฐ์ ํฉ๋๋ค.
๋ค์์ ๊ณ ๋ คํ์ญ์์ค.
class MyDescriptor:
def __get__(self, instance, owner):
print("Descriptor __get__ called")
return "from descriptor"
class MyClassWithSlots:
my_attr = MyDescriptor()
__slots__ = ('my_attr',)
def __init__(self):
# If my_attr were just a regular attribute, this would fail.
# Because MyDescriptor is a descriptor, it intercepts the assignment.
self.my_attr = "instance value"
instance = MyClassWithSlots()
print(instance.my_attr)
instance.my_attr์ ์ ๊ทผํ๋ฉด MyDescriptor.__get__ ๋ฉ์๋๊ฐ ํธ์ถ๋ฉ๋๋ค. self.my_attr = "instance value"๋ฅผ ํ ๋นํ๋ฉด descriptor์ __set__ ๋ฉ์๋(์๋ ๊ฒฝ์ฐ)๊ฐ ํธ์ถ๋ฉ๋๋ค. ๋ฐ์ดํฐ descriptor๊ฐ ์ ์๋๋ฉด ํด๋น ์์ฑ์ ๋ํ ์ง์ ์ฌ๋กฏ ํ ๋น์ ํจ๊ณผ์ ์ผ๋ก ์ฐํํฉ๋๋ค.
์ฑ๋ฅ ์ํฅ: __slots__์ descriptor๋ฅผ ๊ฒฐํฉํ๋ฉด ๊ฐ๋ ฅํ ์ฑ๋ฅ ์ต์ ํ๊ฐ ๋ ์ ์์ต๋๋ค. ๋๋ถ๋ถ์ ์์ฑ์ ๋ํด __slots__์ ๋ฉ๋ชจ๋ฆฌ ์ด์ ์ ์ป๋ ๋์์ ํน์ ์์ฑ์ ๋ํ ์ ํจ์ฑ ๊ฒ์ฌ, ๊ณ์ฐ๋ properties ๋๋ ์ง์ฐ ๋ก๋ฉ๊ณผ ๊ฐ์ ๊ณ ๊ธ ๊ธฐ๋ฅ์ descriptor๋ฅผ ๊ณ์ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์ด๋ฅผ ํตํด ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋๊ณผ ์์ฑ ์ ๊ทผ์ ์ธ๋ถ์ ์ผ๋ก ์ ์ดํ ์ ์์ต๋๋ค.
3. ๋ฉํํด๋์ค ๋ฐ Descriptor
ํด๋์ค ์์ฑ์ ์ ์ดํ๋ ๋ฉํํด๋์ค๋ descriptor์ ํจ๊ป ์ฌ์ฉํ์ฌ descriptor๋ฅผ ํด๋์ค์ ์๋์ผ๋ก ์ฝ์ ํ ์ ์์ต๋๋ค. ์ด๊ฒ์ ๋ ๊ณ ๊ธ ๊ธฐ์ ์ด์ง๋ง ๋๋ฉ์ธ ํน์ ์ธ์ด(DSL)๋ฅผ ๋ง๋ค๊ฑฐ๋ ์ฌ๋ฌ ํด๋์ค์์ ํน์ ํจํด์ ์ ์ฉํ๋ ๋ฐ ๋งค์ฐ ์ ์ฉํ ์ ์์ต๋๋ค.
์๋ฅผ ๋ค์ด, ๋ฉํํด๋์ค๋ ํด๋์ค ๋ณธ๋ฌธ์ ์ ์๋ ์์ฑ์ ์ค์บํ๊ณ ํน์ ํจํด๊ณผ ์ผ์นํ๋ ๊ฒฝ์ฐ ์ ํจ์ฑ ๊ฒ์ฌ ๋๋ ๋ก๊น ์ ์ํด ํน์ descriptor๋ก ์๋์ผ๋ก ๋ํํ ์ ์์ต๋๋ค.
class LoggingDescriptor:
def __init__(self, name):
self.name = name
self._data = {}
def __get__(self, instance, owner):
print(f"Accessing {self.name}...")
return self._data.get(self.name, None)
def __set__(self, instance, value):
print(f"Setting {self.name} to {value}...")
self._data[self.name] = value
class LoggableMetaclass(type):
def __new__(cls, name, bases, dct):
for attr_name, attr_value in dct.items():
# If it's a regular attribute, wrap it in a logging descriptor
if not isinstance(attr_value, (staticmethod, classmethod)) and not attr_name.startswith('__'):
dct[attr_name] = LoggingDescriptor(attr_name)
return super().__new__(cls, name, bases, dct)
class UserProfile(metaclass=LoggableMetaclass):
username = "default_user"
age = 0
def __init__(self, username, age):
self.username = username
self.age = age
# Usage
profile = UserProfile("global_user", 30)
print(profile.username) # Triggers __get__ from LoggingDescriptor
profile.age = 31 # Triggers __set__ from LoggingDescriptor
๊ธ๋ก๋ฒ ๊ด์ : ์ด ํจํด์ ๊ฐ์ฌ ์ถ์ ์ด ์ค์ํ ๊ธ๋ก๋ฒ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋งค์ฐ ์ ์ฉํ ์ ์์ต๋๋ค. ๋ฉํํด๋์ค๋ ๋ค์ํ ๋ชจ๋ธ์ ๋ชจ๋ ์ค์ํ ์์ฑ์ด ์ ๊ทผํ๊ฑฐ๋ ์์ ๋ ๋ ์๋์ผ๋ก ๋ก๊น ๋๋๋ก ๋ณด์ฅํ์ฌ ํน์ ๋ชจ๋ธ ๊ตฌํ์ ๊ด๊ณ์์ด ์ผ๊ด๋ ๊ฐ์ฌ ๋ฉ์ปค๋์ฆ์ ์ ๊ณตํ ์ ์์ต๋๋ค.
4. Descriptor๋ฅผ ์ฌ์ฉํ ์ฑ๋ฅ ํ๋
descriptor๋ฅผ ์ฌ์ฉํ ๋ ์ฑ๋ฅ์ ์ต๋ํํ๋ ค๋ฉด:
- `__get__`์ ๋ก์ง ์ต์ํ:
__get__์ ๋น์ฉ์ด ๋ง์ด ๋๋ ์์ (์: ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ, ๋ณต์กํ ๊ณ์ฐ)์ด ํฌํจ๋ ๊ฒฝ์ฐ ๊ฒฐ๊ณผ๋ฅผ ์บ์ํ๋ ๊ฒ์ ๊ณ ๋ คํ์ญ์์ค. ๊ณ์ฐ๋ ๊ฐ์ ์ธ์คํด์ค์ ๋์ ๋๋ฆฌ ๋๋ descriptor ์์ฒด์์ ๊ด๋ฆฌํ๋ ์ ์ฉ ์บ์์ ์ ์ฅํ์ญ์์ค. - ์ง์ฐ ์ด๊ธฐํ: ๊ฑฐ์ ์ ๊ทผํ์ง ์๊ฑฐ๋ ์์ฑํ๋ ๋ฐ ๋ฆฌ์์ค๊ฐ ๋ง์ด ๋๋ ์์ฑ์ ๊ฒฝ์ฐ descriptor ๋ด์์ ์ง์ฐ ๋ก๋ฉ์ ๊ตฌํํ์ญ์์ค. ์ฆ, ์์ฑ์ ๊ฐ์ ์ฒ์ ์ ๊ทผํ ๋๋ง ๊ณ์ฐ๋๊ฑฐ๋ ๊ฐ์ ธ์ต๋๋ค.
- ํจ์จ์ ์ธ ๋ฐ์ดํฐ ๊ตฌ์กฐ: descriptor๊ฐ ๋ฐ์ดํฐ ์ปฌ๋ ์ ์ ๊ด๋ฆฌํ๋ ๊ฒฝ์ฐ ์์ ์ ๊ฐ์ฅ ํจ์จ์ ์ธ Python ๋ฐ์ดํฐ ๊ตฌ์กฐ(์: `dict`, `set`, `tuple`)๋ฅผ ์ฌ์ฉํ๊ณ ์๋์ง ํ์ธํ์ญ์์ค.
- ๋ถํ์ํ ์ธ์คํด์ค ๋์
๋๋ฆฌ ๋ฐฉ์ง: ๊ฐ๋ฅํ ๊ฒฝ์ฐ descriptor ๊ธฐ๋ฐ ๋์์ด ํ์ํ์ง ์์ ์์ฑ์ ๋ํด
__slots__๋ฅผ ํ์ฉํ์ญ์์ค. - ์ฝ๋ ํ๋กํ์ผ๋ง: ํ๋กํ์ผ๋ง ๋๊ตฌ(`cProfile`๊ณผ ๊ฐ์)๋ฅผ ์ฌ์ฉํ์ฌ ์ค์ ์ฑ๋ฅ ๋ณ๋ชฉ ํ์์ ์๋ณํ์ญ์์ค. ์กฐ๊ธฐ์ ์ต์ ํํ์ง ๋ง์ญ์์ค. descriptor ๊ตฌํ์ ์ํฅ์ ์ธก์ ํ์ญ์์ค.
๊ธ๋ก๋ฒ Descriptor ๊ตฌํ์ ์ํ ๋ชจ๋ฒ ์ฌ๋ก
๊ธ๋ก๋ฒ ์ฌ์ฉ์๋ฅผ ๋์์ผ๋ก ํ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐํ ๋ Descriptor Protocol์ ์ ์คํ๊ฒ ์ ์ฉํ๋ ๊ฒ์ด ์ผ๊ด์ฑ, ์ ์ฉ์ฑ ๋ฐ ์ฑ๋ฅ์ ๋ณด์ฅํ๋ ๋ฐ ์ค์ํฉ๋๋ค.
- ๊ตญ์ ํ(i18n) ๋ฐ ์ง์ญํ(l10n): descriptor๋ฅผ ์ฌ์ฉํ์ฌ ์ง์ญํ๋ ๋ฌธ์์ด ๊ฒ์, ๋ ์ง/์๊ฐ ํ์ ์ง์ ๋ฐ ํตํ ๋ณํ์ ๊ด๋ฆฌํ์ญ์์ค. ์๋ฅผ ๋ค์ด, descriptor๋ ์ฌ์ฉ์ ๋ก์ผ์ผ ์ค์ ์ ๊ธฐ๋ฐ์ผ๋ก UI ์์์ ์ฌ๋ฐ๋ฅธ ๋ฒ์ญ์ ๊ฐ์ ธ์ค๋ ์ญํ ์ ํ ์ ์์ต๋๋ค.
- ๋ค์ํ ์ ๋ ฅ์ ๋ํ ๋ฐ์ดํฐ ์ ํจ์ฑ ๊ฒ์ฌ: descriptor๋ ๋ค์ํ ์ง์ญ์์ ๋ค์ํ ํ์์ผ๋ก ๋ค์ด์ฌ ์ ์๋ ์ฌ์ฉ์ ์ ๋ ฅ(์: ์ ํ ๋ฒํธ, ์ฐํธ ๋ฒํธ, ๋ ์ง)์ ๊ฒ์ฆํ๋ ๋ฐ ํ์ํฉ๋๋ค. descriptor๋ ์ด๋ฌํ ์ ๋ ฅ์ ์ผ๊ด๋ ๋ด๋ถ ํ์์ผ๋ก ์ ๊ทํํ ์ ์์ต๋๋ค.
- ๊ตฌ์ฑ ๊ด๋ฆฌ: ์ง์ญ ๋๋ ๋ฐฐํฌ ํ๊ฒฝ์ ๋ฐ๋ผ ๋ค๋ฅผ ์ ์๋ ์ ํ๋ฆฌ์ผ์ด์ ์ค์ ์ ๊ด๋ฆฌํ๊ธฐ ์ํด descriptor๋ฅผ ๊ตฌํํ์ญ์์ค. ์ด๋ฅผ ํตํด ํต์ฌ ์ ํ๋ฆฌ์ผ์ด์ ๋ก์ง์ ๋ณ๊ฒฝํ์ง ์๊ณ ๋ ๋์ ๊ตฌ์ฑ ๋ก๋ฉ์ด ๊ฐ๋ฅํฉ๋๋ค.
- ์ธ์ฆ ๋ฐ ๊ถํ ๋ถ์ฌ ๋ก์ง: descriptor๋ฅผ ์ฌ์ฉํ์ฌ ์ค์ํ ์์ฑ์ ๋ํ ์ ๊ทผ์ ์ ์ดํ์ฌ ๊ถํ์ด ์๋ ์ฌ์ฉ์(์ ์ฌ์ ์ผ๋ก ์ง์ญ๋ณ ๊ถํ์ด ์๋ ์ฌ์ฉ์)๋ง ํน์ ๋ฐ์ดํฐ๋ฅผ ๋ณด๊ฑฐ๋ ์์ ํ ์ ์๋๋ก ๋ณด์ฅํ์ญ์์ค.
- ๊ธฐ์กด ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํ์ฉ: ๋ง์ ์ฑ์ํ Python ๋ผ์ด๋ธ๋ฌ๋ฆฌ(์: ๋ฐ์ดํฐ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ์ํ Pydantic, ORM์ ์ํ SQLAlchemy)๋ ์ด๋ฏธ Descriptor Protocol์ ๋ง์ด ํ์ฉํ๊ณ ์ถ์ํํฉ๋๋ค. descriptor๋ฅผ ์ดํดํ๋ฉด ์ด๋ฌํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ณด๋ค ํจ๊ณผ์ ์ผ๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค.
๊ฒฐ๋ก
Descriptor Protocol์ Python์ ๊ฐ์ฒด ์งํฅ ๋ชจ๋ธ์ ์ด์์ด๋ฉฐ ์์ฑ ์ ๊ทผ์ ์ฌ์ฉ์ ์ ์ํ๋ ๊ฐ๋ ฅํ๊ณ ์ ์ฐํ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค. ์ฝ๊ฐ์ ์ค๋ฒํค๋๋ฅผ ๋ฐ์์ํค์ง๋ง ์ฝ๋ ๊ตฌ์ฑ, ์ ์ง ๊ด๋ฆฌ์ฑ, ์ ํจ์ฑ ๊ฒ์ฌ, ์ง์ฐ ๋ก๋ฉ ๋ฐ ๋์ ๋์๊ณผ ๊ฐ์ ์ ๊ตํ ๊ธฐ๋ฅ์ ๊ตฌํํ ์ ์๋ค๋ ์ ์์ ์ด์ ์ ๋งค์ฐ ํฝ๋๋ค.
๊ธ๋ก๋ฒ ์ ํ๋ฆฌ์ผ์ด์
์ ๊ตฌ์ถํ๋ ๊ฐ๋ฐ์์๊ฒ descriptor๋ฅผ ๋ง์คํฐํ๋ ๊ฒ์ ๋ ์ฐ์ํ Python ์ฝ๋๋ฅผ ์์ฑํ๋ ๊ฒ๋ฟ๋ง ์๋๋ผ ๊ตญ์ ํ, ์ง์ญํ ๋ฐ ๋ค์ํ ์ฌ์ฉ์ ์๊ตฌ ์ฌํญ์ ๋ณต์ก์ฑ์ ๋ณธ์ง์ ์ผ๋ก ์ ์ํ ์ ์๋ ์์คํ
์ ์ค๊ณํ๋ ๊ฒ์
๋๋ค. __get__, __set__ ๋ฐ __delete__ ๋ฉ์๋๋ฅผ ์ดํดํ๊ณ ์ ๋ต์ ์ผ๋ก ์ ์ฉํ๋ฉด ์๋นํ ์ฑ๋ฅ ํฅ์์ ๋ฌ์ฑํ๊ณ ๋ณด๋ค ํ๋ ฅ์ ์ด๊ณ ์ฑ๋ฅ์ด ๋ฐ์ด๋๋ฉฐ ์ ์ธ๊ณ์ ์ผ๋ก ๊ฒฝ์๋ ฅ ์๋ Python ์ ํ๋ฆฌ์ผ์ด์
์ ๊ตฌ์ถํ ์ ์์ต๋๋ค.
descriptor์ ํ์ ๋ฐ์๋ค์ด๊ณ ์ฌ์ฉ์ ์ ์ ๊ตฌํ์ ์คํํ๊ณ Python ๊ฐ๋ฐ์ ์๋ก์ด ์ฐจ์์ผ๋ก ๋์ด์ฌ๋ฆฌ์ญ์์ค.