๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Python/Python

[Python ์ค‘๊ธ‰] ์ผ๊ธ‰ํ•จ์ˆ˜(First-class)์™€ ํด๋กœ์ €, ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ

๋ฐ˜์‘ํ˜•

๐Ÿ”Š ํ•ด๋‹น ํฌ์ŠคํŒ…์€ ์ธํ”„๋Ÿฐ์˜ ํŒŒ์ด์ฌ ์ค‘๊ธ‰ ๊ฐ•์˜๋ฅผ ๊ณต๋ถ€ํ•˜๋ฉด์„œ ๋ณต์Šต์ฐจ์›์œผ๋กœ ์ •๋ฆฌํ•œ ํฌ์ŠคํŒ…์ž…๋‹ˆ๋‹ค. ํ•ด๋‹น ๋‚ด์šฉ์€ ์ฃผ๋กœ ๋ณธ์ธ์˜ ๋ณต์Šต์šฉ์ด๋ผ์„œ ์ž์„ธํ•œ ์„ค๋ช…์€ ์—†๋Š” ์  ์–‘ํ•ด ๋ถ€ํƒ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค. ์›๋ณธ ์ฝ”๋“œ๋Š” ์—ฌ๊ธฐ์— ์žˆ์Šต๋‹ˆ๋‹ค.

 

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ํŒŒ์ด์ฌ์˜ ์ผ๊ธ‰ํ•จ์ˆ˜(์ผ๊ธ‰๊ฐ์ฒด)์˜ ํŠน์ง•์— ๋Œ€ํ•ด ๊ฐ„๋‹จํžˆ ์•Œ์•„๋ณด๊ณ , ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ํด๋กœ์ €์— ๋Œ€ํ•ด ์‹ค์Šตํ•ด๋ณด๊ณ  ํด๋กœ์ € ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๊นŒ์ง€ ๋งŒ๋“ค์–ด๋ณด๋Š” ์‹ค์Šต์„ ํ•ด๋ณด์ž.

 

Python

 

1. ์ผ๊ธ‰ํ•จ์ˆ˜(First-class)์˜ ํŠน์ง•

ํŒŒ์ด์ฌ ์ผ๊ธ‰ํ•จ์ˆ˜์˜ ํŠน์ง•์œผ๋กœ๋Š” ๋‹ค์Œ์˜ 4๊ฐ€์ง€ ํŠน์ง•์ด ์กด์žฌ

 

  • ๋Ÿฐํƒ€์ž„ ์ดˆ๊ธฐํ™” : ์‹คํ–‰ ์‹œ์ ์— ์ดˆ๊ธฐํ™”๊ฐ€ ๋จ์„ ์˜๋ฏธ
  • ํ•จ์ˆ˜๋ฅผ ๋ณ€์ˆ˜์— ํ• ๋‹น์ด ๊ฐ€๋Šฅ
  • ํ•จ์ˆ˜๋ฅผ ๋˜ ๋‹ค๋ฅธ ํ•จ์ˆ˜ ์ธ์ž๋กœ ์ „๋‹ฌ ๊ฐ€๋Šฅ
  • ํ•จ์ˆ˜ ๊ฒฐ๊ณผ๋ฅผ ๋‹ค๋ฅธ ํ•จ์ˆ˜ ๊ฒฐ๊ณผ๊ฐ’์— ํ• ๋‹น์— ๋ฐ˜ํ™˜ ๊ฐ€๋Šฅ

๋จผ์ € ํ•จ์ˆ˜๋ฅผ ๋ณ€์ˆ˜์— ํ• ๋‹น์ด ๊ฐ€๋Šฅํ•œ ์˜ˆ์‹œ๋ฅผ ๊ฐ„๋‹จํ•˜๊ฒŒ ์‚ดํŽด๋ณด์ž.

 

# ์ผ๋ฐ˜ ํ•จ์ˆ˜ ๋งŒ๋“ค์–ด๋ณด๊ธฐ
def factorial(n):
    """factorial function -> n : int """
    # ์žฌ๊ท€ํ•จ์ˆ˜ ์ข…๋‹จ ์กฐ๊ฑด
    if n < 2:
        return 1
    else:
        return n + factorial(n-1)

# ์ผ๋ฐ˜ Class ๋งŒ๋“ค๊ธฐ
class A:
    pass
    
# ์ผ๊ธ‰๊ฐ์ฒด ํŠน์ง• : ํ•จ์ˆ˜๋ฅผ ๋ณ€์ˆ˜๋กœ ํ• ๋‹น ๊ฐ€๋Šฅ
var_func = factorial
print(type(var_func))
print(var_func(5))
# map ํ•จ์ˆ˜ ์ด์šฉํ•ด๋ณด๊ธฐ
print(list(map(var_func, range(1, 12))))

 

๊ฒฐ๊ณผํ™”๋ฉด

 

์œ„ ํ™”๋ฉด์ฒ˜๋Ÿผ ํ•จ์ˆ˜๋„ ์ผ์ข…์˜ 'function' ์ด๋ผ๋Š” ํด๋ž˜์Šค์ธ ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

 

๋‹ค์Œ์€ ํ•จ์ˆ˜๋ฅผ ๋˜ ๋‹ค๋ฅธ ํ•จ์ˆ˜ ์ธ์ž์— ์ „๋‹ฌํ•˜๋Š” ํŠน์ง•์„ ๋ณด์ž. ์ด๋ฅผ ๊ณ ์œ„(High-order) ํ•จ์ˆ˜๋ผ๊ณ ๋„ ํ•œ๋‹ค.

 

# 3. ์ผ๊ธ‰๊ฐ์ฒด ํŠน์ง• : ํ•จ์ˆ˜๋ฅผ ํ•จ์ˆ˜ ์ธ์ž์— ์ „๋‹ฌ => ๊ณ ์œ„(high-order) ํ•จ์ˆ˜
# filter์—์„œ ์ต๋ช…ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•ด ๋ฆฌ์ŠคํŠธ๋ฅผ ํ•„ํ„ฐ๋งํ•œ ํ›„ ์ด๋ฅผ var_func ํ•จ์ˆ˜์— map์„ ์ด์šฉํ•ด ์ ์šฉ!
print(list(map(var_func, filter(lambda x: x % 2, range(1, 11)))))
print([var_func(i) for i in range(1, 11) if i % 2])

 

์ฆ‰, factorial์ด๋ผ๋Š” ํ•จ์ˆ˜๋ฅผ var_func ๋ณ€์ˆ˜์— ํ• ๋‹นํ•˜๊ณ  ์ด๋ฅผ map ํ•จ์ˆ˜์ธ์ž๋กœ ๋‹ค์‹œ ์ „๋‹ฌํ•œ๋‹ค. ์ถ”๊ฐ€๋กœ ๋ฐฉ๊ธˆ ์šฐ๋ฆฌ๊ฐ€ ์ง์ ‘ ์ •์˜ํ•œ factorial ํ•จ์ˆ˜๊ฐ€ ์•„๋‹Œ ์ด๋ฏธ ๊ตฌํ˜„๋˜์–ด ์žˆ๋Š” ํ•จ์ˆ˜ add๋ฅผ import๋ฅผ ํ•œ ํ›„ reduce ํ•จ์ˆ˜ ์ธ์ž๋กœ ์ „๋‹ฌํ•ด๋ณด์ž. ์ฐธ๊ณ ๋กœ reduce ํ•จ์ˆ˜๋Š” ์ฃผ์–ด์ง„ ๋ฐ์ดํ„ฐ๋ฅผ ๋ˆ„์ ํ•˜๋ฉด์„œ ์ง‘๊ณ„์‹œ์ผœ์ฃผ๋Š” ํ•จ์ˆ˜์ด๋‹ค. 

 

# reduce() ๋ฉ”์†Œ๋“œ ์‚ฌ์šฉ๋ฐฉ๋ฒ• -> reduce(ํ•จ์ˆ˜, ๋ฆฌ์ŠคํŠธ๊ฐ์ฒด)
from functools import reduce
from operator import add

print(reduce(add, range(1, 5)))
print(sum(range(1, 5)))

# ์ต๋ช…ํ•จ์ˆ˜ : lambda -> ๊ฐ€๊ธ‰์  ์ฃผ์„์„ ์‚ฌ์šฉํ•˜๋˜ ๋˜๋„๋ก์ด๋ฉด ์ผ๋ฐ˜ํ•จ์ˆ˜ ํ˜•ํƒœ๋กœ ์ž‘์„ฑํ•˜๋Š”๊ฒŒ ๊ถŒ์žฅ(๊ฐ€๋…์„ฑ ์œ„ํ•ด)
print(reduce(lambda x, t: x + t, range(1, 5)))

 

๋งˆ์ง€๋ง‰์ธ ํ•จ์ˆ˜๋ฅผ ๋˜ ๋‹ค๋ฅธ ํ•จ์ˆ˜์˜ ๊ฒฐ๊ณผ๊ฐ’์œผ๋กœ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์€ ํด๋กœ์ €์™€ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์—์„œ ๋‹ค๋ฃจ๋ฏ€๋กœ ๋” ํ•˜๋‹จ์˜ ๋‚ด์šฉ์—์„œ ๋‹ค๋ฃจ๊ธฐ๋กœ ํ•˜์ž. ํ•œ ๊ฐ€์ง€ ์ถ”๊ฐ€๋กœ ์†Œ๊ฐœํ•  ๋ฉ”์„œ๋“œ๋Š” partial ๋ฉ”์„œ๋“œ๋‹ค. partial ๋ฉ”์„œ๋“œ๋Š” ์ธ์ˆ˜๋ฅผ ๊ณ ์ •์‹œํ‚ค๋Š” ํ•จ์ˆ˜์ธ๋ฐ, ์˜ˆ์‹œ ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด์ž.

 

# partial ์‚ฌ์šฉ๋ฒ• : ์ธ์ˆ˜๋ฅผ ๊ณ ์ •์‹œํ‚ค๊ธฐ -> ์ฝœ๋ฐฑํ•จ์ˆ˜์— ์‚ฌ์šฉ๋จ
# partial(ํ•จ์ˆ˜, ๊ณ ์ •์‹œํ‚ฌ ์ธ์ž)
from operator import mul
from functools import partial

# ์ธ์ˆ˜ ํ•˜๋‚˜๋ฅผ 10์œผ๋กœ ๊ณ ์ •์‹œํ‚ค๊ธฐ
one_fix = partial(mul, 10)
# 10 ์ธ์ˆ˜๋ฅผ ๊ณ ์ •์‹œํ‚จ ์ฑ„ 2๋ผ๋Š” ์ถ”๊ฐ€ ์ธ์ˆ˜๋งŒ ์ง€์ •ํ•ด์ฃผ๊ณ  mul ์—ฐ์‚ฐ ์ˆ˜ํ–‰!
print(one_fix(2))
print([one_fix(i) for i in range(1, 11)])
# map ์ด์šฉํ•ด๋ณด๊ธฐ
print(list(map(one_fix, range(1, 11))))
# filter๋Š” ์ฐธ, ๊ฑฐ์ง“์„ ํŒ๋‹จํ•ด์„œ ์š”์†Œ๋“ค์„ ํ•„ํ„ฐ๋ง ํ•˜๋Š” ์—ญํ• ์ž„!(map๊ณผ๋Š” ๋‹ค๋ฆ„!!)
print(list(filter(lambda x: x % 2, range(1, 11))))
# filter + map์„ ๊ฒฐํ•ฉ!!
print(list(map(one_fix, filter(lambda x: x % 2, range(1, 11)))))
print('-'*50)

# ์ธ์ˆ˜ ํ•˜๋‚˜๋ฅผ ์ถ”๊ฐ€๋กœ ๋˜ ๊ณ ์ • ์‹œํ‚ค๊ธฐ
double_fix = partial(one_fix, 2000)
print(double_fix()) # mul์—ฐ์‚ฐ์— 2๊ฐœ์˜ ์ธ์ˆ˜๊ฐ€ ํ•„์š”ํ•œ๋ฐ ์ด๋ฏธ 2๊ฐœ ์ธ์ˆ˜ ๋ชจ๋‘ ๊ณ ์ •์‹œ์ผฐ์œผ๋ฏ€๋กœ ๊ทธ๋ƒฅ ํ˜ธ์ถœํ•˜๋ฉด ๋จ!
# print(double_fix(4)) # ์ถ”๊ฐ€ ์ธ์ˆ˜๋ฅผ ๋„ฃ์–ด์ฃผ๋ฉด ์—๋Ÿฌ ๋ฐœ์ƒ!

2. ํด๋กœ์ € ๊ฐœ๋…์— ๋Œ€ํ•œ ์ดํ•ด

ํด๋กœ์ € ๊ฐœ๋…์— ๋Œ€ํ•œ ์ดํ•ด๋ฅผ ํ•˜๊ธฐ์ „์— ์šฐ์„  ํŒŒ์ด์ฌ ๋ณŒ์ˆ˜ ๋ฒ”์œ„(scope)์— ๋Œ€ํ•ด ์‚ดํŽด๋ณด๋Š” ๊ฒƒ๋„ ์ข‹์€๋ฐ, ์ด์— ๋Œ€ํ•œ ์ž์„ธํ•œ ์„ค๋ช… ๋ธ”๋กœ๊ทธ๊ฐ€ ์žˆ์–ด ์†Œ๊ฐœํ•œ๋‹ค. ํด๋กœ์ €๋Š” ์ผ๋ช… '์ƒํƒœ๋ฅผ ๊ณ„์† ์ €์žฅํ•˜๊ณ  ์œ ์ง€'ํ•˜๊ธฐ ์œ„ํ•œ๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์ฆ‰, ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ณต์œ ํ•˜์ง€ ์•Š๊ณ  ๋ฉ”์„ธ์ง€ ์ „๋‹ฌ๋กœ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•จ์ด๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ ํด๋กœ์ €๋Š”  ์ถ”ํ›„์— ๋ฐฐ์šธ ๋ฉ€ํ‹ฐ ์“ฐ๋ ˆ๋“œ(์ฝ”๋ฃจํ‹ด) ํ”„๋กœ๊ทธ๋ž˜๋ฐ์— ๊ฐ•ํ•˜๋‹ค๊ณ  ํ•œ๋‹ค. ๋ณดํ†ต ํด๋กœ์ €๋Š” ๊ณต์œ ๋ฅผ ํ•˜๋˜ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š”(immutable) ๊ฒƒ๋“ค์„ ์œ„ํ•ด ์ ๊ทน์ ์œผ๋กœ ์‚ฌ์šฉ๋œ๋‹ค.

 

์šฐ์„ , ์ผ๋ฐ˜์ ์œผ๋กœ ๋ˆ„์ ํ•ฉ์„ ๊ตฌํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ class๋ฅผ ์ด์šฉํ•ด ๋ˆ„์ ํ•ฉ์„ ๊ตฌํ•˜๋Š” ์‹ค์Šต ์ฝ”๋“œ์ด๋‹ค.

 

# ์ผ๋ฐ˜์ ์ธ ๋ˆ„์ ํ•ฉ ๊ตฌํ•˜๊ธฐ
print(sum(range(1,11)))
print()

# ๋ˆ„์  ํ‰๊ท  ๊ตฌํ•˜๋Š” Class ์ •์˜ํ•ด๋ณด๊ธฐ
# ํด๋ž˜์Šค์˜ ๋งค์ง ๋ฉ”์„œ๋“œ __call__์€ ํด๋ž˜์Šค๋ฅผ ๋งˆ์น˜ ํ•จ์ˆ˜(๋ฉ”์†Œ๋“œ)์ฒ˜๋Ÿผ callableํ•  ์ˆ˜ ์žˆ์Œ!!
class Averager:
    def __init__(self):
        self._series = []

    def __call__(self, value):
        self._series.append(value)
        print(f"Series:{self._series}, Length:{len(self._series)}")
        # ๋ˆ„์  ํ‰๊ท ๊ฐ’ return
        return sum(self._series) / len(self._series)
# ํด๋ž˜์Šค๋ฅผ ์ด์šฉํ•ด ๋ˆ„์ ๋œ ๊ฐ’ save
cls_averager = Averager()
result = cls_averager(10)
print("1๋ฒˆ์งธ:", result)

# ์œ„์— save๋œ ์ƒํƒœ์—์„œ ๋ˆ„์ ์‹œํ‚ค๊ณ  ํ‰๊ท ๊ตฌํ•˜๊ธฐ
result = cls_averager(20)
print("2๋ฒˆ์งธ:", result)
result = cls_averager(154)
print("3๋ฒˆ์งธ:", result)
result = cls_averager(256)
print("4๋ฒˆ์งธ:", result)

 

๋‹ค์Œ์€ ํด๋กœ์ €๋ฅผ ์ด์šฉํ•ด์„œ ๋ˆ„์ ํ•ฉ์„ ๊ตฌํ•ด๋ณด์ž. ์šฐ์„  ํด๋กœ์ €๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ์™ธ๋ถ€(outer) ํ•จ์ˆ˜์™€ ๋‚ด๋ถ€(inner ์ด๊ฒƒ์„ nested function์ด๋ผ๊ณ ๋„ ํ•จ)ํ•จ์ˆ˜๊ฐ€ ์กด์žฌํ•˜๊ณ  ์ด๋ฅผ ํ™œ์šฉํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์—ผ๋‘์— ๋‘์ž.

 

def outer_func():
     series = [] # Free variable(์ž์œ  ๋ณ€์ˆ˜) => list, dict, set๊ฐ™์€ ๊ฒฝ์šฐ mutableํ•˜๊ธฐ ๋•Œ๋ฌธ์— nonlocal ๋”ฐ๋กœ ์„ ์–ธํ•˜์ง€ ์•Š์•„๋„ ๊ฐ€๋Šฅ!
     def inner_func(value):
         series.append(value)
         print(f"Series:{series}, Length:{len(series)}")
         return sum(series) / len(series)
     # inner_func์„ return ํ•จ์œผ๋กœ์จ ์ƒํƒœ๋ฅผ ๊ณ„์† save!
     return inner_func

avg_closure1 = outer_func()
print(type(avg_closure1))
print("1๋ฒˆ์งธ ํ˜ธ์ถœ:", avg_closure1(10))
print("2๋ฒˆ์งธ ํ˜ธ์ถœ:", avg_closure1(20))
print("3๋ฒˆ์งธ ํ˜ธ์ถœ:", avg_closure1(55))

 

๊ฒฐ๊ณผํ™”๋ฉด

 

๊ฒฐ๊ณผํ™”๋ฉด์—์„œ ํด๋กœ์ €ํ•จ์ˆ˜์˜ type์„ ์‚ดํŽด๋ณด๋ฉด function์ด๋ผ๋Š” ์ผ์ข…์˜ ํด๋ž˜์Šค์ธ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ๋˜ ํ•œ๊ฐ€์ง€ ์—ผ๋‘์— ๋‘˜ ์ ์€ outer_func๊ณผ inner_fun(์ค‘์ฒฉํ•จ์ˆ˜) ์‚ฌ์ด๋ฅผ ๋ณดํ†ต Free variable(์ž์œ  ๋ณ€์ˆ˜) ๊ตฌ์—ญ์ด๋ผ๊ณ  ์นญํ•˜๋Š”๋ฐ, ์—ฌ๊ธฐ์„œ ์ •์˜๋˜๋Š” ๋ณ€์ˆ˜๋‚˜ ํ•จ์ˆ˜๋“ค์€ ์ค‘์ฒฉํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ Free variable ๊ตฌ์—ญ ๋‚ด์—์„œ ์ •์˜๋˜๋Š” ์ž๋ฃŒ๊ตฌ์กฐ๊ฐ€ ๋งŒ์•ฝ immutableํ•œ ์ž๋ฃŒ๊ตฌ์กฐ(int, float, string, tuple, bool, frozenset)๋ผ๋ฉด ์ค‘์ฒฉํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ nonlocal๋กœ ๋ณ€์ˆ˜๋ฅผ ์ •์˜ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

 

# ์ž˜๋ชป๋œ Closure ์‚ฌ์šฉ๋ฒ•
def outer_func2():
    # Free variable ์„ ์–ธ
    cnt = 0
    total = 0
    def inner_func2(value):
        cnt += 1 # inner_func2 ์™ธ๋ถ€์— ์žˆ๋Š” cnt, total ๋ณ€์ˆ˜๋ฅผ ์ฐธ์กฐํ•˜์ง€ ๋ชปํ•จ! => cnt, total์ด immtuableํ•œ ์ž๋ฃŒ๊ตฌ์กฐ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ถˆ๊ฐ€!
        total += value
        return total / cnt
    return inner_func2
    
# ์—๋Ÿฌ๋ฐœ์ƒ
wrong_closure = outer_func2() 
wrong_closure(10) 

 

# ์œ„ ์ž˜๋ชป๋œ ๋ฐฉ๋ฒ• ํ•ด๊ฒฐ ๋ฐฉ์•ˆ
def outer_func3():
    cnt = 0 # Free variable ์„ ์–ธ
    total = 0
    def inner_func3(value):
        # cnt, total์ด inner_func3 ํ•จ์ˆ˜ ์™ธ๋ถ€์˜ ์ง€์—ญ๋ณ€์ˆ˜๋ฅผ ์ฐธ์กฐํ•  ๊ฒƒ์„ ์„ ์–ธ!
        nonlocal cnt, total
        cnt += 1
        total += value
        return total / cnt
    return inner_func3

right_closure = outer_func3()
print("1๋ฒˆ์งธ ํ˜ธ์ถœ:", right_closure(10))
print("2๋ฒˆ์งธ ํ˜ธ์ถœ:", right_closure(20))
print("3๋ฒˆ์งธ ํ˜ธ์ถœ:", right_closure(55))

๊ฒฐ๊ณผํ™”๋ฉด์„ ๋ณด๋ฉด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋ˆ„์  ํ‰๊ท ๊ฐ’์ด ์ž˜ ์ถœ๋ ฅ๋˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

 

๊ฒฐ๊ณผํ™”๋ฉด

3. ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ

์ด์ „์— ๋ฐฐ์› ๋˜ ํด๋กœ์ € ํ•จ์ˆ˜๋ฅผ ํ™œ์šฉํ•ด ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž. ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ ํด๋กœ์ € ํ•จ์ˆ˜์˜ ์™ธ๋ถ€ํ•จ์ˆ˜์— ์ธ์ž๋กœ ํŠน์ •ํ•œ ํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ์ „๋‹ฌํ•˜์ž.(์ด ๋•Œ ํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์€ ์ผ๊ธ‰ ๊ฐ์ฒด์˜ ํŠน์ง•!)

 

๋จผ์ € ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์˜ ์žฅ,๋‹จ์ ์— ๋Œ€ํ•ด์„œ ๊ฐ„๋‹จํžˆ ์•Œ์•„๋ณด์ž.

 

  • ์žฅ์ 
    1. ์ค‘๋ณต ์ œ๊ฑฐ, ์ฝ”๋“œ์˜ ๊ฐ„๊ฒฐ, ๊ณตํ†ต ํ•จ์ˆ˜ ์ž‘์„ฑ(๊ณตํ†ต ํ•จ์ˆ˜๋ž€, ๋กœ๊น…, ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ณ , ์œ ํšจ์„ฑ ์ฒดํฌ ๊ฐ€๋Šฅ. ๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๊ฐ€ ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ํ…์„œํ”Œ๋กœ์šฐ ์†Œ์Šค์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด ๋งŽ์€ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๊ฐ€ ์‚ฌ์šฉ๋จ)
  • ๋‹จ์ 
    1. ๋„ˆ๋ฌด ๋‚จ๋ฐœํ•˜๋ฉด ๊ฐ€๋…์„ฑ ๊ฐ์†Œ.. ๊ทธ๋ž˜์„œ ๋””๋ฒ„๊น…์ด ์–ด๋ ค์›€
    2. ํŠน์ • ๊ธฐ๋Šฅ์— ํ•œ์ •๋œ ํ•จ์ˆ˜๋Š” ๊ตณ์ด ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ณด๋‹ค ์ผ๋ฐ˜ ๋‹จ์ผ ํ•จ์ˆ˜๋กœ ์ •์˜ํ•˜๋Š” ๊ฒƒ์ด ๋‚˜์€ ๊ฒฝ์šฐ๋„ ์กด์žฌ

์ด์ œ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์‹ค์Šต์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด์ž. ํŠน์ • ํ•จ์ˆ˜๊ฐ€ ๋™์ž‘๋˜๊ณ  ์ข…๋ฃŒ๋˜๋Š” ๋™์•ˆ์˜ ์‹œ๊ฐ„์ธ ๋Ÿฐํƒ€์ž„์„ ์ธก์ •ํ•ด๋ณด๋Š” ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž

 

# ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์‹ค์Šต - ํŠน์ • ํ•จ์ˆ˜๊ฐ€ ๋™์ž‘๋˜๊ณ  ์ข…๋ฃŒ๋˜๋Š” ๋™์•ˆ์˜ ์‹œ๊ฐ„์„ ์ธก์ •ํ•ด๋ณด์ž
import time
# ์šฐ์„ , ํด๋กœ์ € ํ•จ์ˆ˜ ๋งŒ๋“ค๊ธฐ
def perf_clock(func): # ํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ์ „๋‹ฌ - ์ผ๊ธ‰ ๊ฐ์ฒด์˜ ํŠน์ง•
    # Free variable ๊ตฌ์—ญ
    def inner_func(*args): # ์œ ๋™์ ์ธ ์ธ์ž๋ฅผ ์œ„ํ•œ packing
        # ์‹œ์ž‘ time ์ธก์ •
        st = time.perf_counter()
        result = func(*args)
        et = time.perf_counter()
        runtime = et - st
        name = func.__name__
        arg_str = ', '.join(repr(arg) for arg in args)
        print(f"{runtime :.5f} {name}({arg_str}) -> {result}")
        return result
    return inner_func
    
# 1. ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์‚ฌ์šฉํ•ด๋ณด๊ธฐ
@perf_clock
def sleep_func(seconds):
    time.sleep(seconds)
    pass

@perf_clock
def sum_func(*numbers):
    return sum(numbers)
print("## Decorator๋กœ sleep_func ํ•จ์ˆ˜ ๋™์ž‘ ์‹œ๊ฐ„ ์ถœ๋ ฅ ##")
result1 = sleep_func(2)
print("## Decorator๋กœ sum_func ํ•จ์ˆ˜ ๋™์ž‘ ์‹œ๊ฐ„ ์ถœ๋ ฅ ##")
result2 = sum_func(1,2,3,4,5,6,7,8,9,)
print(result2)

 

๋ฒˆ์™ธ๋กœ ๋งŒ์•ฝ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ํ˜ธ์ถœํ•œ๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ํด๋กœ์ € ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•˜๋Š” ๋ถ€๋ถ„์€ ๋™์ผํ•˜๋‹ค.

 

import time
# ์šฐ์„ , ํด๋กœ์ € ํ•จ์ˆ˜ ๋งŒ๋“ค๊ธฐ
def perf_clock(func): # ํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ์ „๋‹ฌ - ์ผ๊ธ‰ ๊ฐ์ฒด์˜ ํŠน์ง•
    # Free variable ๊ตฌ์—ญ
    def inner_func(*args): # ์œ ๋™์ ์ธ ์ธ์ž๋ฅผ ์œ„ํ•œ packing
        # ์‹œ์ž‘ time ์ธก์ •
        st = time.perf_counter()
        result = func(*args)
        et = time.perf_counter()
        runtime = et - st
        name = func.__name__
        arg_str = ', '.join(repr(arg) for arg in args)
        print(f"{runtime :.5f} {name}({arg_str}) -> {result}")
        return result
    return inner_func

# ๋™์ž‘ ์‹œ๊ฐ„์„ ์ธก์ •ํ•  ํ•จ์ˆ˜ 2๊ฐ€์ง€ ์ •์˜ํ•ด์ฃผ๊ธฐ
def sleep_func(seconds):
    time.sleep(seconds)
    return

def sum_func(*numbers):
    return sum(numbers)

# 2. ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ํ˜ธ์ถœํ•˜๊ธฐ
no_deco1 = perf_clock(sleep_func)
print("## None Decorator๋กœ sleep_func ํ•จ์ˆ˜ ๋™์ž‘ ์‹œ๊ฐ„ ์ถœ๋ ฅ ##")
result1 = no_deco1(2)

no_deco2 = perf_clock(sum_func)
print("## None Decorator๋กœ sum_func ํ•จ์ˆ˜ ๋™์ž‘ ์‹œ๊ฐ„ ์ถœ๋ ฅ ##")
result2 = no_deco2(1,2,3,4,5,6,7,8,9,10)
print("sum_func ํ•จ์ˆ˜ ๊ฒฐ๊ณผ :", result2)
๋ฐ˜์‘ํ˜•