๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ Python

์ดํŽ™ํ‹ฐ๋ธŒ ํŒŒ์ด์ฌ 6์žฅ - ๋ฉ”ํƒ€ํด๋ž˜์Šค์™€ ์• ํŠธ๋ฆฌ๋ทฐํŠธ

by dev.py 2025. 3. 30.

Better way 44 ์„ธํ„ฐ์™€ ๊ฒŒํ„ฐ ๋ฉ”์„œ๋“œ ๋Œ€์‹  ํ‰๋ฒ”ํ•œ ์• ํŠธ๋ฆฌ๋ทฐํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ผ

 

setter, getter ๋ฉ”์„œ๋“œ ํ™œ์šฉ์ด ์•„๋‹Œ ์• ํŠธ๋ฆฌ๋ทฐํŠธ๋ฅผ ์ง์ ‘ ํ™œ์šฉํ•˜์ž

# ์•ˆ ์ข‹์€ ์˜ˆ์‹œ - getter, setter ํ™œ์šฉ
class Person:
    def __init__(self):
        self._name = ''

    def get_name(self):
        return self._name

    def set_name(self, value):
        self._name = value

p = Person()
p.set_name('Sungchan')
print(p.get_name())

 

# ์ข‹์€ ์˜ˆ์‹œ - ์• ํŠธ๋ฆฌ๋ทฐํŠธ ํ™œ์šฉ
class Person:
    def __init__(self):
        self.name = ''

p = Person()
p.name = 'Sungchan'
print(p.name)

 

๋งŒ์•ฝ์— ํŠน์ • ์• ํŠธ๋ฆฌ๋ทฐํŠธ๊ฐ€ ์„ค์ •๋  ๋•Œ, ํŠน๋ณ„ํ•œ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค๋ฉด @property๋ฅผ ํ™œ์šฉํ•˜์ž

class Person:
    def __init__(self):
        self._age = 0

    @property # ์• ํŠธ๋ฆฌ๋ทฐํŠธ ๊ฐ’ ์กฐํšŒ ์‹œ, ํ•ด๋‹น ํ•จ์ˆ˜ ํ˜ธ์ถœ
    def age(self):
        return self._age

    @age.setter # age ์• ํŠธ๋ฆฌ๋ทฐํŠธ ๊ฐ’ ๋ณ€๊ฒฝ ์‹œ, ํ•ด๋‹น ํ•จ์ˆ˜ ํ˜ธ์ถœ
    def age(self, value):
        if value < 0:
            raise ValueError("๋‚˜์ด๋Š” ์Œ์ˆ˜๊ฐ€ ๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
        self._age = value

p = Person()
p.age = 25
print(p.age)  # 25

p.age = -3  # ValueError: ๋‚˜์ด๋Š” ์Œ์ˆ˜๊ฐ€ ๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

 

 

@property ์‚ฌ์šฉ์‹œ ์ฃผ์˜์ 

  • I/O ๋˜๋Š” DB ์กฐํšŒ์™€ ๊ฐ™์€ ์ˆ˜ํ–‰ ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๊ฑฐ๋‚˜ ๋น„์šฉ์ด ๋น„์‹ผ ๋‚ด์šฉ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • ๋‹ค๋ฅธ ๊ฐ์ฒด๋ฅผ ์กฐํšŒํ•˜๊ฑฐ๋‚˜ ํฌํ•จํ•˜๋Š” ๋ณต์žกํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์ง€ ์•Š๋Š”๋‹ค.

-> ๊ฐœ๋ฐœ์ž๊ฐ€ ์ธ์‹ํ•˜๊ธฐ ์–ด๋ ค์›€, ์ผ๋ฐ˜ ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์ž

 

 

 

Better way 45 ์• ํŠธ๋ฆฌ๋ทฐํŠธ๋ฅผ ๋ฆฌํŒฉํ„ฐ๋งํ•˜๋Š” ๋Œ€์‹  @property๋ฅผ ์‚ฌ์šฉํ•˜๋ผ

์ƒํ’ˆ์˜ ๊ฐ€๊ฒฉ๊ณผ ํ• ์ธ ์ •์ฑ…์„ ๊ด€๋ฆฌํ•˜๋Š” ๊ฐ์ฒด๊ฐ€ ์žˆ๋‹ค.

๊ฐ€๊ฒฉ๊ณผ ํ• ์ธ ์ •์ฑ…์ด ํ•ญ์ƒ ์—ฐ๋™๋˜๋Š” ๊ฐ์ฒด๊ฐ€ ํ•„์š”ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜์ž.

class Product:
    def __init__(self, price):
        self._price = price
        self._discount_rate = 0.1  # ๊ธฐ๋ณธ ํ• ์ธ์œจ 10%

    @property
    def price(self):
        return int(self._price * (1 - self._discount_rate))

    @price.setter
    def price(self, value):
        if value < 0:
            raise ValueError("๊ฐ€๊ฒฉ์€ ์Œ์ˆ˜๊ฐ€ ๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
        self._price = value

    @property
    def discount_rate(self):
        return self._discount_rate

    @discount_rate.setter
    def discount_rate(self, value):
        if not 0 <= value <= 1:
            raise ValueError("ํ• ์ธ์œจ์€ 0~1 ์‚ฌ์ด์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.")
        self._discount_rate = value

# ์‚ฌ์šฉ ์˜ˆ์‹œ
p = Product(10000)
print(p.price)  # 9000 (10% ํ• ์ธ)

p.discount_rate = 0.2
print(p.price)  # 8000 (20% ํ• ์ธ)

p.price = 20000
print(p.price)  # 16000 (20% ํ• ์ธ ์ ์šฉ๋œ ์ƒˆ ๊ฐ€๊ฒฉ)

 

 

Better way 46 ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ @property ๋ฉ”์„œ๋“œ๋ฅผ ๋งŒ๋“ค๋ ค๋ฉด ๋””์Šคํฌ๋ฆฝํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ผ

@propery์˜ ๋‹จ์ ์œผ๋กœ๋Š” ์žฌ์‚ฌ์šฉ์„ฑ์ด ์–ด๋ ต๋‹ค.

๊ฐ๊ฐ์˜ property ๋งˆ๋‹ค @propery, @property.setter ์— ํ•ด๋‹นํ•˜๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•ด์•ผํ•œ๋‹ค.

๋™์ผํ•œ ๋กœ์ง, ๋‚ด์šฉ์˜ property๋ฅผ ํ™œ์šฉํ•  ๋•Œ๋Š” ๋””์Šคํฌ๋ฆฝํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์ž.

# ์•ˆ ์ข‹์€ ์˜ˆ์‹œ - propery ๊ฐ๊ฐ ๊ตฌํ˜„
class Product:
    def __init__(self, price, weight):
        self._price = price
        self._weight = weight

    @property
    def price(self):
        return self._price

    @price.setter
    def price(self, value):
        if value < 0:
            raise ValueError("๊ฐ€๊ฒฉ์€ ์Œ์ˆ˜์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
        self._price = value

    @property
    def weight(self): # price property ์™€ ๋กœ์ง ์ค‘๋ณต
        return self._weight

    @weight.setter
    def weight(self, value): # price property ์™€ ๊ฒ€์ฆ ๋กœ์ง ์ค‘๋ณต
        if value < 0:
            raise ValueError("๋ฌด๊ฒŒ๋Š” ์Œ์ˆ˜์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
        self._weight = value

 

class Positive: # ๋””์Šคํฌ๋ฆฝํ„ฐ
    def __set_name__(self, owner, name):
        self.name = f"_{name}"

    def __get__(self, instance, owner): # ๋””์Šคํฌ๋ฆฝํ„ฐ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด __get__, __set__ ๊ตฌํ˜„ ํ•„์ˆ˜ 
        return getattr(instance, self.name)

    def __set__(self, instance, value): # ๋””์Šคํฌ๋ฆฝํ„ฐ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด __get__, __set__ ๊ตฌํ˜„ ํ•„์ˆ˜ 
        if value < 0:
            raise ValueError(f"{self.name[1:]}๋Š”(์€) ์Œ์ˆ˜์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
        setattr(instance, self.name, value)


class Product:
    price = Positive() # Positive ๋””์Šคํฌ๋ฆฝํ„ฐ ํ™œ์šฉ
    weight = Positive() # Positive ๋””์Šคํฌ๋ฆฝํ„ฐ ํ™œ์šฉ

    def __init__(self, price, weight):
        self.price = price
        self.weight = weight
        
p = Product(10000, 2.5)
print(p.price)   # 10000
print(p.weight)  # 2.5

p.price = 5000   # ์ •์ƒ
# p.weight = -1  # ValueError: weight๋Š”(์€) ์Œ์ˆ˜์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

 

 

Better way 47 ์ง€์—ฐ ๊ณ„์‚ฐ ์• ํŠธ๋ฆฌ๋ทฐํŠธ๊ฐ€ ํ•„์š”ํ•˜๋ฉด __getattr__, __getattribute__, __setattr__์„ ์‚ฌ์šฉํ•˜๋ผ

 

๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ์‹œ์  ์šฉ๋„
__getattribute__ ๋ชจ๋“  ์†์„ฑ ์ ‘๊ทผ ์‹œ ๋ฌด์กฐ๊ฑด ํ˜ธ์ถœ ์†์„ฑ ์ ‘๊ทผ ์ „์ฒ˜๋ฆฌ, ๊ฐ์‹œ, ๋กœ๊น… ๋“ฑ
__getattr__ ์†์„ฑ์ด ์กด์žฌํ•˜์ง€ ์•Š์„ ๋•Œ๋งŒ ํ˜ธ์ถœ ๋™์  ์†์„ฑ ์ƒ์„ฑ, ์ง€์—ฐ ๊ณ„์‚ฐ ๋“ฑ
__setattr__ ๋ชจ๋“  ์†์„ฑ ์„ค์ • ์‹œ ํ˜ธ์ถœ ๊ฐ’ ๊ฒ€์ฆ, ์ž๋™ ๋กœ๊ทธ, ๋‚ด๋ถ€ ์ €์žฅ ๋ฐฉ์‹ ๋ณ€๊ฒฝ ๋“ฑ

 

class DynamicAttrs: # ์• ํŠธ๋ฆฌ๋ทฐํŠธ๊ฐ€ ์—†์œผ๋ฉด ๋™์ ์œผ๋กœ ์• ํŠธ๋ฆฌ๋ทฐํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ํด๋ž˜์Šค
    def __init__(self):
        self.existing = 42

    def __getattribute__(self, name): # ์• ํŠธ๋ฆฌ๋ทฐํŠธ ์ ‘๊ทผ ์‹œ - ๋ฌด์กฐ๊ฑด ํ˜ธ์ถœ
        print(f"[getattribute] {name} ์ ‘๊ทผ ์‹œ๋„")
        return super().__getattribute__(name)  # ๋ฌดํ•œ ์žฌ๊ท€ ๋ฐฉ์ง€!

    def __getattr__(self, name): # ์• ํŠธ๋ฆฌ๋ทฐํŠธ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์„ ๋•Œ
        print(f"[getattr] {name} ์†์„ฑ์ด ์กด์žฌํ•˜์ง€ ์•Š์•„ ๋™์ ์œผ๋กœ ์ƒ์„ฑ")
        value = f"{name}์„ ์œ„ํ•œ ๊ฐ’" # ์ž„์‹œ ๋ณ€์ˆ˜ ๊ฐ’ ์ƒ์„ฑ 
        setattr(self, name, value) # ์• ํŠธ๋ฆฌ๋ทฐํŠธ ์ถ”๊ฐ€
        return value

    def __setattr__(self, name, value): # ์• ํŠธ๋ฆฌ๋ทฐํŠธ ๊ฐ’ ์ˆ˜์ • ์‹œ ํ˜ธ์ถœ
        print(f"[setattr] {name} ์„ค์ •: {value}")
        super().__setattr__(name, value)
        
        

obj = DynamicAttrs()

# ์กด์žฌํ•˜๋Š” ์†์„ฑ ์ ‘๊ทผ
print(obj.existing)
# => [getattribute] existing ์ ‘๊ทผ ์‹œ๋„
# => 42

# ์กด์žฌํ•˜์ง€ ์•Š๋Š” ์†์„ฑ ์ ‘๊ทผ โ†’ getattr ์ž‘๋™
print(obj.missing)
# => [getattribute] missing ์ ‘๊ทผ ์‹œ๋„
# => [getattr] missing ์†์„ฑ์ด ์กด์žฌํ•˜์ง€ ์•Š์•„ ๋™์ ์œผ๋กœ ์ƒ์„ฑ
# => missing์„ ์œ„ํ•œ ๊ฐ’

# ๋‹ค์‹œ ์ ‘๊ทผ ์‹œ โ†’ ์ด๋ฏธ ์กด์žฌํ•˜๋ฏ€๋กœ getattr์€ ํ˜ธ์ถœ๋˜์ง€ ์•Š์Œ
print(obj.missing)
# => [getattribute] missing ์ ‘๊ทผ ์‹œ๋„
# => missing์„ ์œ„ํ•œ ๊ฐ’

 

 

Better way 48 __init_subclass__๋ฅผ ์‚ฌ์šฉํ•ด ํ•˜์œ„ ํด๋ž˜์Šค๋ฅผ ๊ฒ€์ฆํ•˜๋ผ

์ƒ์†๋ฐ›๋Š” ํ•˜์œ„ ํด๋ž˜์Šค๊ฐ€ ํŠน์ • ๊ทœ์น™์„ ๋”ฐ๋ฅด๋„๋ก ๊ฐ•์ œํ•˜๊ณ  ์‹ถ์„ ๋•Œ, __init_subclass__๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์ถ”์ƒ ๋ฉ”์†Œ๋“œ๋ฅผ ์„ ์–ธํ•˜๊ณ  ํ•˜์œ„ ํด๋ž˜์Šค์—์„œ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ์œ ์—ฐํ•œ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.

๋ฏน์Šค์ธ ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๊ธฐ๋„ ํ›จ์”ฌ ์ข‹๋‹ค.

 

๋‘ ๊ฐ€์ง€ ๋ถ€๋ชจ ํด๋ž˜์Šค๊ฐ€ ์žˆ๋‹ค.

1. ๋‹ค๊ฐํ˜•์ด ์œ ํšจํ•œ์ง€ ๊ฒ€์‚ฌ (sides >= 3)

2. ์ƒ‰์ด ํŒŒ๋ž€์ƒ‰์„ ์ผ๋Š”์ง€ ๊ฒ€์‚ฌ (color == 'blue')

 

class Polygon: # sides ๊ฐ€ 3๊ฐœ ์ด์ƒ์ธ์ง€ ํ™•์ธ
    def __init_subclass__(cls):
        super().__init_subclass__()
        if not hasattr(cls, 'sides'):
            raise TypeError(f"{cls.__name__}๋Š” 'sides' ์†์„ฑ์„ ์ •์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.")
        if cls.sides < 3:
            raise ValueError(f"{cls.__name__}๋Š” ์ตœ์†Œ 3๊ฐœ์˜ ๋ณ€์„ ๊ฐ€์ ธ์•ผ ํ•ฉ๋‹ˆ๋‹ค.")
            
class Triangle(Polygon):
    sides = 3  # โœ… ์œ ํšจ

class Line(Polygon):
    sides = 1  # โŒ ValueError: Line๋Š” ์ตœ์†Œ 3๊ฐœ์˜ ๋ณ€์„ ๊ฐ€์ ธ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

 

๋ฏน์Šค์ธ ์˜ˆ์‹œ

class BlueShape:
    def __init_subclass__(cls):
        super().__init_subclass__()
        if not hasattr(cls, 'color'):
            raise TypeError(f"{cls.__name__}๋Š” 'color' ์†์„ฑ์„ ์ •์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.")
        if cls.color != 'blue':
            raise ValueError(f"{cls.__name__}๋Š” color๊ฐ€ ๋ฐ˜๋“œ์‹œ 'blue'์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.")


class BlueSquare(Polygon, BlueShape): # ๋ฏน์Šค์ธํ•˜์—ฌ ๋‘ ๊ฐ€์ง€ ํด๋ž˜์Šค ๊ฒ€์ฆ ๊ฐ€๋Šฅ
    sides = 4
    color = 'blue'  # โœ… ์œ ํšจ

class RedSquare(Polygon, BlueShape): # ๋ฏน์Šค์ธํ•˜์—ฌ ๋‘ ๊ฐ€์ง€ ํด๋ž˜์Šค ๊ฒ€์ฆ ๊ฐ€๋Šฅ
    sides = 4
    color = 'red'  # โŒ ValueError: RedSquare๋Š” color๊ฐ€ ๋ฐ˜๋“œ์‹œ 'blue'์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

Better way 49 __init_subclass__๋ฅผ ์‚ฌ์šฉํ•ด ํด๋ž˜์Šค ํ™•์žฅ์„ ๋“ฑ๋กํ•˜๋ผ

ํ”Œ๋Ÿฌ๊ทธ์ธ, ๋ช…๋ น์–ด, ํ•ธ๋“ค๋Ÿฌ ๋“ฑ์„ ๋งŒ๋“ค ๋•Œ, ์ƒ์†์œผ๋กœ ํ•˜๋ฉด ์ž๋™์œผ๋กœ ๋“ฑ๋ก๋˜๋Š” ๊ตฌ์กฐ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

class CommandBase: # ๋ช…๋ น์–ด ์ง‘ํ•ฉ
    registry = {}

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        name = cls.__name__.lower().replace("command", "")
        CommandBase.registry[name] = cls
        
class HelloCommand(CommandBase): # CommandBase๋ฅผ ์ƒ์† -> CommandBase.registry์— ๋“ฑ๋ก
    def run(self): 
        print("์•ˆ๋…•ํ•˜์„ธ์š”!")

class ExitCommand(CommandBase): # CommandBase๋ฅผ ์ƒ์† -> CommandBase.registry์— ๋“ฑ๋ก
    def run(self):
        print("์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค.")

 

# ๋“ฑ๋ก๋œ ์ปค๋งจ๋“œ ํ™•์ธ
print(CommandBase.registry)
# {'hello': <class '__main__.HelloCommand'>, 'exit': <class '__main__.ExitCommand'>}

# ์ปค๋งจ๋“œ ์‹คํ–‰
CommandBase.registry['hello']().run()  # ์•ˆ๋…•ํ•˜์„ธ์š”!
CommandBase.registry['exit']().run()   # ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค.

 

 

 

Better way 51 ํ•ฉ์„ฑ ๊ฐ€๋Šฅํ•œ ํด๋ž˜์Šค ํ™•์žฅ์ด ํ•„์š”ํ•˜๋ฉด ๋ฉ”ํƒ€ํด๋ž˜์Šค๋ณด๋‹ค๋Š” ํด๋ž˜์Šค ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ผ

๋น„๊ต ํ•ญ๋ชฉ ํด๋ž˜์Šค ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ (Python) ๋ฉ”ํƒ€ํด๋ž˜์Šค (Python) ์–ด๋…ธํ…Œ์ด์…˜ (Java)
์ ์šฉ ๋Œ€์ƒ ํด๋ž˜์Šค ์ „์ฒด ํด๋ž˜์Šค ์ „์ฒด ํด๋ž˜์Šค, ๋ฉ”์„œ๋“œ, ํ•„๋“œ ๋“ฑ
์‚ฌ์šฉ ๋ฐฉ์‹ @decorator metaclass=... @AnnotationName
์‚ฌ์šฉ ์‹œ์  ํด๋ž˜์Šค ์ •์˜ ํ›„
- ํด๋ž˜์Šค ์ „์ฒด๊ฐ€ ๋งŒ๋“ค์–ด์ง„ ๋‹ค์Œ
@decorator(cls) ํ˜ธ์ถœ
ํด๋ž˜์Šค ์ •์˜ ์ค‘
- type.__new__ ์ด๋‚˜ __init__ ์—์„œ
ํด๋ž˜์Šค ์ƒ์„ฑ ๊ณผ์ •์—์„œ ๊ด€์—ฌ
์ปดํŒŒ์ผ ์‹œ / ๋Ÿฐํƒ€์ž„ ์‹œ
๊ธฐ๋Šฅ ๋ณ€๊ฒฝ ๋ฐฉ์‹ ๋ฉ”์„œ๋“œ ์ถ”๊ฐ€, ์†์„ฑ ๋ณ€๊ฒฝ ๋“ฑ ๋™์  ํ™•์žฅ ํด๋ž˜์Šค ์ƒ์„ฑ ๋ฐฉ์‹ ๋ณ€๊ฒฝ ๋ฆฌํ”Œ๋ ‰์…˜ ๊ธฐ๋ฐ˜ ์ฒ˜๋ฆฌ (ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ํ•ด์„ํ•จ)
ํ•ฉ์„ฑ ๊ฐ€๋Šฅ์„ฑ O (์—ฌ๋Ÿฌ ๊ฐœ ์ค‘์ฒฉ ๊ฐ€๋Šฅ) X (ํ•˜๋‚˜๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ) O (์—ฌ๋Ÿฌ ์–ด๋…ธํ…Œ์ด์…˜ ๊ฐ€๋Šฅ)

 

 

ํด๋ž˜์Šค ๋‚ด ๋ฉ”์„œ๋“œ์˜ ๋™์ž‘์„ ๋กœ๊น…ํ•˜๋Š” ํด๋ž˜์Šค ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ

def enhance_class(cls): # ํด๋ž˜์Šค ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ, ๋ฉ”์†Œ๋“œ ๋™์ž‘์„ ๋กœ๊น…ํ•จ
    # ๋ชจ๋“  ๋ฉ”์„œ๋“œ์— ๋กœ๊น… ์ถ”๊ฐ€
    for attr_name, attr_value in cls.__dict__.items():
        if callable(attr_value):
            def wrapper(func):
                def wrapped(self, *args, **kwargs):
                    print(f"[LOG] {func.__name__} called")
                    return func(self, *args, **kwargs)
                return wrapped
            setattr(cls, attr_name, wrapper(attr_value))

    # ํด๋ž˜์Šค์— ๊ณตํ†ต ์†์„ฑ ์ถ”๊ฐ€
    cls.created_by = "system"
    return cls

 

@enhance_class
class User:
    def __init__(self, name):
        self.name = name

    def greet(self):
        print(f"Hello, {self.name}")
        

u = User("pasa")
u.greet()
print(u.created_by)


# ์ถœ๋ ฅ
# [LOG] __init__ called
# [LOG] greet called
# Hello, pasa
# system

 

์ •๋ฆฌ

๋น„๊ต ํด๋ž˜์Šค ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ๋ฉ”ํƒ€ํด๋ž˜์Šค
ํ˜ธ์ถœ ์‹œ์  ํด๋ž˜์Šค ์ •์˜ ํ›„ ํด๋ž˜์Šค ์ •์˜ ์ค‘
๊ฐœ์ž… ๋Œ€์ƒ ์™„์„ฑ๋œ ํด๋ž˜์Šค ๊ฐ์ฒด ํด๋ž˜์Šค ์ƒ์„ฑ ํ”„๋กœ์„ธ์Šค
์œ ์—ฐ์„ฑ ๊ฐ„๋‹จํ•œ ํ™•์žฅ, ํ•ฉ์„ฑ์— ์œ ๋ฆฌ ๊ตฌ์กฐ์ ์ธ ์ œ์–ด์— ๊ฐ•๋ ฅ
๋‚œ์ด๋„ ๋‚ฎ์Œ ๋†’์Œ