[ํŒŒ์ด์ฌ] ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ(Decorator) ์‚ฌ์šฉ๋ฒ•

Posted by Euisuk's Dev Log on December 25, 2024

[ํŒŒ์ด์ฌ] ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ(Decorator) ์‚ฌ์šฉ๋ฒ•

์›๋ณธ ๊ฒŒ์‹œ๊ธ€: https://velog.io/@euisuk-chung/Python-Decorator-Intro

๋ฉ”๋ฆฌํฌ๋ฆฌ์Šค๋งˆ์Šค ์—ฌ๋Ÿฌ๋ถ„!!โ€งโ‚Šหš๐ŸŽ„โœฉ ํฌ๋ฆฌ์Šค๋งˆ์Šค ํŠธ๋ฆฌ๋ฅผ ์žฅ์‹ํ•  ๋•Œ, ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๊ฑด ๋ญ๋‹ˆ ๋ญ๋‹ˆ ํ•ด๋„ ํŠธ๋ฆฌ ์ž์ฒด์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋‹จ์ˆœํ•œ ๋‚˜๋ฌด์— ์˜ˆ์œ ์ „๊ตฌ, ๋ฆฌ๋ณธ, ์žฅ๋‚œ๊ฐ์„ ๋”ํ•˜๋ฉด ํŠน๋ณ„ํ•œ ๋ถ„์œ„๊ธฐ๋ฅผ ์—ฐ์ถœํ•  ์ˆ˜ ์žˆ์ฃ . ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค! ๊ธฐ๋ณธ ํ•จ์ˆ˜์— ์›ํ•˜๋Š” ์žฅ์‹์„ ๋ง๋ถ™์—ฌ์„œ, ํ•จ์ˆ˜์˜ ์ „ํ›„๋ฅผ ๊พธ๋ฏธ๊ฑฐ๋‚˜ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋ฒˆ ํฌ์ŠคํŠธ์—์„œ๋Š” ํฌ๋ฆฌ์Šค๋งˆ์Šค ํŠธ๋ฆฌ๋ฅผ ๊พธ๋ฏธ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ, ํ•จ์ˆ˜์— ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ๋”ํ•ด๋ณด๋Š” ์žฌ๋ฏธ์žˆ๋Š” ์—ฌ์ •์„ ์‹œ์ž‘ํ•ด ๋ด…์‹œ๋‹ค. ํ•จ์ˆ˜ ํ˜ธ์ถœ ์ „ํ›„๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜, ์‹คํ–‰ ์‹œ๊ฐ„์„ ์ธก์ •ํ•˜๊ฑฐ๋‚˜, ๋กœ๊ทธ๋ฅผ ๋‚จ๊ธฐ๊ฑฐ๋‚˜, ์ธ์ฆ ์ฒดํฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๋“ฑ ๋‹ค์–‘ํ•œ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํ™œ์šฉ๋ฒ•์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ๋ถ„์˜ ์ฝ”๋“œ๊ฐ€ ๋”์šฑ ๋น›๋‚˜๊ฒŒ ๋  ๊ฑฐ์˜ˆ์š”! โœจ

ํŒŒ์ด์ฌ์˜ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ(Decorator)๋Š” โ€œํ•จ์ˆ˜๋‚˜ ๋ฉ”์„œ๋“œ์˜ ๋™์ž‘์„ ํ™•์žฅํ•˜๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋Š” ๋งค์šฐ ๊ฐ•๋ ฅํ•œ ๋„๊ตฌโ€์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ทธ ๊ฐœ๋…์ด ์ƒ์†Œํ•˜๊ฑฐ๋‚˜ ์ฝ”๋“œ ๊ตฌ์กฐ๊ฐ€ ๋‚ฏ์„ค์–ด ์ฒ˜์Œ ๋ฐฐ์šฐ๋Š” ์‚ฌ๋žŒ์—๊ฒŒ๋Š” ์–ด๋ ต๊ฒŒ ๋А๊ปด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์ด๋ฒˆ ๊ธ€์—์„œ๋Š” ์ดˆ๋ณด์ž๋„ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์˜ ๊ธฐ๋ณธ ๊ฐœ๋…๊ณผ ํ™œ์šฉ๋ฒ•์„ ์‰ฝ๊ณ  ๋ช…ํ™•ํ•˜๊ฒŒ ์„ค๋ช…ํ•˜๊ณ , ์—ฌ๋Ÿฌ ์˜ˆ์ œ๋ฅผ ํ†ตํ•ด ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์˜ ์œ ์šฉ์„ฑ์„ ๋ณด์—ฌ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

1. ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ž€ ๋ฌด์—‡์ธ๊ฐ€์š”?

์šฐ๋ฆฌ๊ฐ€ โ€œ๋ฌด์—‡์ธ๊ฐ€๋ฅผ decorateํ•˜๋‹คโ€๋ผ๊ณ  ํ•˜๋ฉด, โ€œ๋ฌด์—‡์ธ๊ฐ€๋ฅผ ์žฅ์‹ํ•˜๋‹ค, ๋˜๋Š” ๊พธ๋ฏธ๋‹คโ€๋ผ๋Š” ์˜๋ฏธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

=> ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ(decorator)๋Š” ๋ง ๊ทธ๋Œ€๋กœ ํ•จ์ˆ˜๋ฅผ ๊พธ๋ฉฐ์ฃผ๋Š” ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. ์ฆ‰, ์–ด๋–ค ํ•จ์ˆ˜๊ฐ€ ์žˆ์„ ๋•Œ ํ•ด๋‹น ํ•จ์ˆ˜๋ฅผ ์ง์ ‘ ์ˆ˜์ •ํ•˜์ง€ ์•Š๊ณ  ํ•จ์ˆ˜๋ฅผ ๊พธ๋ฉฐ์ฃผ๊ธฐ ์œ„ํ•ด ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • ์ •์˜:
    • ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” ํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ๋ฐ›์•„ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•œ ํ›„, ์ˆ˜์ •๋œ ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ณ ์ฐจ ํ•จ์ˆ˜(higher-order function)์ž…๋‹ˆ๋‹ค.

โ“ ๊ณ ์ฐจ ํ•จ์ˆ˜(Higher-Order Function)๋Š” ๋‹ค์Œ ๋‘ ๊ฐ€์ง€ ์กฐ๊ฑด ์ค‘ ํ•˜๋‚˜ ์ด์ƒ์„ ๋งŒ์กฑํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋งํ•ฉ๋‹ˆ๋‹ค:

  • (1) ํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜
    • ๋‹ค๋ฅธ ํ•จ์ˆ˜(ํ˜น์€ ๋žŒ๋‹ค ํ•จ์ˆ˜ ๋“ฑ)๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›์•„ ์‹คํ–‰ํ•˜๊ฑฐ๋‚˜ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์˜ˆ: map(), filter(), reduce() ๋“ฑ์ด ์ด์— ํ•ด๋‹นํ•ฉ๋‹ˆ๋‹ค.
  • (2) ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜
    • ๋‹ค๋ฅธ ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜๊ฐ’์œผ๋กœ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜ ๋ฆฌํ„ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

(1) ํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ๋ฐ›๋Š” ์˜ˆ์‹œ

1
2
3
4
5
6
7
  def apply_function(func, data):
      return func(data)
  # ์‚ฌ์šฉ ์˜ˆ์‹œ
  def square(x):
      return x * x
  result = apply_function(square, 5)  # 25
  print(result)
  • apply_function์€ func์ด๋ผ๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ํ•จ์ˆ˜๋ฅผ ๋ฐ›์Šต๋‹ˆ๋‹ค.
  • ํ˜ธ์ถœ ์‹œ, square๋ผ๋Š” ํ•จ์ˆ˜๋ฅผ ๋„˜๊ฒจ์ฃผ๊ณ  data๋กœ 5๋ฅผ ์ „๋‹ฌํ•˜์—ฌ square(5)๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

(2) ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์˜ˆ์‹œ

1
2
3
4
5
6
7
  def outer_function(message):
      def inner_function():
          return f"Message: {message}"
      return inner_function
  # ์‚ฌ์šฉ ์˜ˆ์‹œ
  my_function = outer_function("Hello, World!")
  print(my_function())  # Message: Hello, World!
  • outer_function์€ inner_function์ด๋ผ๋Š” ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  • ๋ฐ˜ํ™˜๋œ my_function์€ ๋‚ด๋ถ€์˜ inner_function ์—ญํ• ์„ ํ•˜๋ฉฐ, ๋‚˜์ค‘์— ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
  • ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” ๊ณ ์ฐจ ํ•จ์ˆ˜์˜ ๋‘ ๋ฒˆ์งธ ํŠน์ง•(ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜)์„ ์ด์šฉํ•ฉ๋‹ˆ๋‹ค.

    • (์„ค๋ช…1) ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” ํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ๋ฐ›๊ณ , ๊ทธ ํ•จ์ˆ˜๋ฅผ ์ˆ˜์ •ํ•˜๊ฑฐ๋‚˜ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•œ ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜์—ฌ ๊ธฐ์กด ํ•จ์ˆ˜๋ฅผ โ€œ์žฅ์‹โ€ํ•ฉ๋‹ˆ๋‹ค.
    • (์„ค๋ช…2) ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” ํ•จ์ˆ˜๋ฅผ ๋ฐ›์•„์„œ ๋‚ด๋ถ€์— ์ƒˆ๋กœ์šด ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•˜๊ณ , ์ด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๊ธฐ์กด ํ•จ์ˆ˜์— ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์žฅ์ :

    • ์ฝ”๋“œ ์ˆ˜์ • ์—†์ด ํ•จ์ˆ˜์˜ ๋™์ž‘์„ ๋ณ€๊ฒฝํ•˜๊ฑฐ๋‚˜ ์ถ”๊ฐ€์ ์ธ ๊ธฐ๋Šฅ์„ ๋ถ€์—ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋ฐ˜๋ณต ์ฝ”๋“œ๋ฅผ ์ค„์ด๊ณ , ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ๋†’์ž…๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ํ•จ์ˆ˜ ํ˜ธ์ถœ ์ „ํ›„์— ๋กœ๊ทธ๋ฅผ ์ถœ๋ ฅํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, ํ•ด๋‹น ์ฝ”๋“œ๋ฅผ ๋ชจ๋“  ํ•จ์ˆ˜์— ์‚ฝ์ž…ํ•˜์ง€ ์•Š๊ณ  ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋กœ ๊น”๋”ํ•˜๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


2. ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

2.1 ๊ธฐ๋ณธ ๊ตฌ์กฐ

๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์˜ ๊ธฐ๋ณธ ๊ตฌ์กฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

1
2
3
4
5
6
7
def my_decorator(func):  # func: ๊พธ๋ฏธ๊ณ ์ž ํ•˜๋Š” ํ•จ์ˆ˜
    def wrapper():
        print("Before the function is called")
        func()  # ์›๋ž˜ ํ•จ์ˆ˜ ์‹คํ–‰
        print("After the function is called")
    return wrapper  # ์ˆ˜์ •๋œ ํ•จ์ˆ˜ ๋ฐ˜ํ™˜

  • my_decorator(func): ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋กœ ์‚ฌ์šฉ๋  ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
    • ์ด ํ•จ์ˆ˜๋Š” ๋‹ค๋ฅธ ํ•จ์ˆ˜๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜(func)๋กœ ๋ฐ›์•„, ์›๋ž˜ ํ•จ์ˆ˜(func)์˜ ๋™์ž‘์„ ๊ฐ์‹ธ๋Š” wrapper ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•˜๊ณ  ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
    • wrapper(): ์›๋ž˜ ํ•จ์ˆ˜(func) ํ˜ธ์ถœ ์ „ํ›„์— ์ถ”๊ฐ€ ๋™์ž‘์„ ์‚ฝ์ž…ํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

2.2 ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์ ์šฉ

๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” @๋ฅผ ์‚ฌ์šฉํ•ด ๊ฐ„ํŽธํ•˜๊ฒŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

1
2
3
4
5
6
@my_decorator
def say_hello():
    print("Hello, World!")

say_hello()

  • @my_decorator: say_hello ํ•จ์ˆ˜์— my_decorator๋ฅผ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.
    • say_hello๋Š” ์ด์ œ my_decorator(say_hello)๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” wrapper ํ•จ์ˆ˜๋กœ ๋Œ€์ฒด๋ฉ๋‹ˆ๋‹ค.
  • ๊ฒฐ๊ณผ์ ์œผ๋กœ say_hello()๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด wrapper()๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

์ฆ‰, ์œ„ ์ฝ”๋“œ๋Š” ์•„๋ž˜ ์ฝ”๋“œ์™€ ๋™์ผํ•˜๊ฒŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค:

1
2
3
say_hello = my_decorator(say_hello)
say_hello()

  • ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์œ„์ฒ˜๋Ÿผ ์ˆ˜๋™์œผ๋กœ ํ•จ์ˆ˜์— ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์ ์šฉํ•ด๋„ ๋™์ผํ•œ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ต๋‹ˆ๋‹ค.
  • ์›๋ž˜ say_hello ํ•จ์ˆ˜๋Š” my_decorator๋ฅผ ํ†ตํ•ด ๊ฐ์‹ธ์ธ wrapper๋กœ ๋Œ€์ฒด๋ฉ๋‹ˆ๋‹ค.

2.3 ์‹คํ–‰ ๊ฒฐ๊ณผ

1
say_hello()
  • ํ•จ์ˆ˜ ํ˜ธ์ถœ ํ๋ฆ„:

    1. wrapper ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
    2. print("Before the function is called")๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
    3. ์›๋ž˜ say_hello ํ•จ์ˆ˜ (func)๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ print("Hello, World!")๊ฐ€ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.
    4. print("After the function is called")๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
  • ์ตœ์ข… ์ถœ๋ ฅ:

1
2
3
Before the function is called
Hello, World!
After the function is called

3. ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์˜ˆ์ œ๋กœ ๋ฐฐ์šฐ๊ธฐ

3.1 ๊ฐ„๋‹จํ•œ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์˜ˆ์ œ

ํ•จ์ˆ˜ ํ˜ธ์ถœ ์ „ํ›„์— ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ๋งŒ๋“ค์–ด ๋ด…์‹œ๋‹ค.

  • ์ฒซ ๋ฒˆ์งธ ์˜ˆ์ œ๋Š” ์œ„์— 2๋ฒˆ์—์„œ ๋ณธ ์˜ˆ์‹œ์™€ ์œ ์‚ฌํ•œ ์œ ํ˜•์ž…๋‹ˆ๋‹ค.
1
2
3
4
5
6
7
8
9
10
11
12
def my_decorator(func):
    def wrapper():
        print("Before the function is called")
        func()
        print("After the function is called")
    return wrapper

@my_decorator
def greet():
    print("Hello!")

greet()
  • my_decorator:

    • ์ด ํ•จ์ˆ˜๋Š” ์›๋ž˜ ํ•จ์ˆ˜๋ฅผ ๊ฐ์‹ธ๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. ํ˜ธ์ถœ ์ „ํ›„์— ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•˜์—ฌ ํ•จ์ˆ˜ ๋™์ž‘์„ ๋ณด๊ฐ•ํ•ฉ๋‹ˆ๋‹ค.
  • greet:

    • ๋‹จ์ˆœํžˆ โ€œHello!โ€๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ํ•จ์ˆ˜์ด๋‚˜, @my_decorator ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ํ†ตํ•ด ํ˜ธ์ถœ ์ „ํ›„์— ๋ฉ”์‹œ์ง€๊ฐ€ ์ถ”๊ฐ€๋กœ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.

์ถœ๋ ฅ ๊ฒฐ๊ณผ:

1
2
3
Before the function is called
Hello!
After the function is called

3.2 ์ธ์ž์™€ ๋ฐ˜ํ™˜๊ฐ’ ์ฒ˜๋ฆฌ

๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” *args์™€ **kwargs๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค์–‘ํ•œ ์ธ์ž์™€ ๋ฐ˜ํ™˜๊ฐ’์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("Function is about to execute")
        result = func(*args, **kwargs)  # ์›๋ž˜ ํ•จ์ˆ˜ ํ˜ธ์ถœ
        print("Function has finished executing")
        return result
    return wrapper

@my_decorator
def add(a, b):
    return a + b

print(add(3, 4))
  • wrapper: ์ธ์ž์™€ ๋ฐ˜ํ™˜๊ฐ’์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด *args์™€ **kwargs๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด๋กœ ์ธํ•ด ๋‹ค์–‘ํ•œ ํ˜•ํƒœ์˜ ํ•จ์ˆ˜ ํ˜ธ์ถœ์„ ์ง€์›ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • add: ๋‘ ์ˆซ์ž์˜ ํ•ฉ์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ํ†ตํ•ด ํ˜ธ์ถœ ์ „ํ›„์— ๋ฉ”์‹œ์ง€๊ฐ€ ์ถœ๋ ฅ๋˜๋ฉฐ, ๊ฒฐ๊ณผ๊ฐ’๋„ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.

์ถœ๋ ฅ ๊ฒฐ๊ณผ:

1
2
3
Function is about to execute
Function has finished executing
7

4. ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํ™œ์šฉ ์˜ˆ์ œ

4.1 ๋กœ๊น… ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ

์–ด๋–ค ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ ๋กœ๊ทธ๋ฅผ ๋‚จ๊ธฐ๊ณ  ์‹ถ์„ ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import logging
logging.basicConfig(level=logging.INFO)

def log_decorator(func):
    def wrapper(*args, **kwargs):
        logging.info(f"Calling {func.__name__} with {args} and {kwargs}")
        result = func(*args, **kwargs)
        logging.info(f"{func.__name__} finished executing")
        return result
    return wrapper

@log_decorator
def multiply(a, b):
    return a * b

print(multiply(5, 3))
  • log_decorator: ํ•จ์ˆ˜ ํ˜ธ์ถœ ์‹œ ๋กœ๊ทธ ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. ์ด๋กœ ์ธํ•ด ๋””๋ฒ„๊น…์ด๋‚˜ ํ•จ์ˆ˜ ํ˜ธ์ถœ ์ถ”์ ์— ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • multiply: ๋‘ ์ˆซ์ž๋ฅผ ๊ณฑํ•˜๋Š” ํ•จ์ˆ˜๋กœ, ํ˜ธ์ถœ ์‹œ ๋กœ๊ทธ๊ฐ€ ์ž๋™์œผ๋กœ ๊ธฐ๋ก๋ฉ๋‹ˆ๋‹ค.

์ถœ๋ ฅ ๊ฒฐ๊ณผ:

1
2
3
INFO:root:Calling multiply with (5, 3) and {}
INFO:root:multiply finished executing
15

4.2 ์‹คํ–‰ ์‹œ๊ฐ„ ์ธก์ • ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ

ํ•จ์ˆ˜ ์‹คํ–‰์— ๊ฑธ๋ฆฐ ์‹œ๊ฐ„์„ ์ธก์ •ํ•˜๋Š” ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ๋งŒ๋“ค์–ด ๋ด…์‹œ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import time

def timing_decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()  # ์‹œ์ž‘ ์‹œ๊ฐ„ ๊ธฐ๋ก
        result = func(*args, **kwargs)
        end_time = time.time()  # ์ข…๋ฃŒ ์‹œ๊ฐ„ ๊ธฐ๋ก
        print(f"{func.__name__} took {end_time - start_time:.4f} seconds to execute")
        return result
    return wrapper

@timing_decorator
def slow_function():
    time.sleep(2)
    print("Finished sleeping")

slow_function()
  • timing_decorator: ํ•จ์ˆ˜ ์‹คํ–‰ ์‹œ์ž‘๊ณผ ์ข…๋ฃŒ ์‹œ๊ฐ์„ ๊ธฐ๋กํ•˜์—ฌ ์‹คํ–‰ ์‹œ๊ฐ„์„ ์ธก์ •ํ•ฉ๋‹ˆ๋‹ค.
  • slow_function: 2์ดˆ๊ฐ„ ๋ฉˆ์ถ˜ ๋’ค ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ํ•จ์ˆ˜๋กœ, ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ํ†ตํ•ด ์‹คํ–‰ ์‹œ๊ฐ„์ด ์ธก์ •๋ฉ๋‹ˆ๋‹ค.

์ถœ๋ ฅ ๊ฒฐ๊ณผ:

1
2
Finished sleeping
slow_function took 2.0002 seconds to execute

4.3 ์ธ์ฆ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ

์‚ฌ์šฉ์ž์˜ ์ธ์ฆ ์ƒํƒœ๋ฅผ ํ™•์ธํ•˜๊ณ , ์ธ์ฆ๋˜์ง€ ์•Š์œผ๋ฉด ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์ž…๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def authenticate_decorator(func):
    def wrapper(user, *args, **kwargs):
        if not user.get("is_authenticated"):
            raise PermissionError("User not authenticated")
        return func(user, *args, **kwargs)
    return wrapper

@authenticate_decorator
def view_profile(user):
    print(f"User profile: {user['name']}")

user = {"name": "Alice", "is_authenticated": True}
view_profile(user)

unauth_user = {"name": "Bob", "is_authenticated": False}
view_profile(unauth_user)  # PermissionError ๋ฐœ์ƒ
  • authenticate_decorator: ์‚ฌ์šฉ์ž์˜ ์ธ์ฆ ์ƒํƒœ๋ฅผ ํ™•์ธํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ธ์ฆ๋˜์ง€ ์•Š์€ ์‚ฌ์šฉ์ž์— ๋Œ€ํ•ด ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.
  • view_profile: ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ์ •๋ณด๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ํ†ตํ•ด ์ธ์ฆ ์ƒํƒœ๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

์ถœ๋ ฅ ๊ฒฐ๊ณผ:

1
2
3
4
User profile: Alice
Traceback (most recent call last):
  ...
PermissionError: User not authenticated

5. ๊ณ ๊ธ‰ ์ฃผ์ œ: functools.wraps

๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์›๋ž˜ ํ•จ์ˆ˜์˜ ์ด๋ฆ„(__name__)๊ณผ ๋ฌธ์„œ ๋ฌธ์ž์—ด(__doc__)์ด ๊ฐ์‹ธ๋Š” ํ•จ์ˆ˜(wrapper)๋กœ ๋ฎ์—ฌ ์›๋ž˜ ํ•จ์ˆ˜์˜ ์ •๋ณด๋ฅผ ์žƒ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด functools.wraps๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("Before function call")
        result = func(*args, **kwargs)
        print("After function call")
        return result
    return wrapper

@my_decorator
def my_function():
    """This is my function."""
    print("Hello!")

print(my_function.__name__)  # my_function
print(my_function.__doc__)   # This is my function.
  • wraps: functools์˜ ์œ ํ‹ธ๋ฆฌํ‹ฐ๋กœ, ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์ ์šฉ ํ›„์—๋„ ์›๋ž˜ ํ•จ์ˆ˜์˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ(__name__, __doc__)๋ฅผ ์œ ์ง€์‹œ์ผœ์ค๋‹ˆ๋‹ค.
  • my_function: ๊ฐ„๋‹จํ•œ ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ํ•จ์ˆ˜๋กœ, ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ํ†ตํ•ด ํ˜ธ์ถœ ์ „ํ›„์— ๋ฉ”์‹œ์ง€๊ฐ€ ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค.

์ถœ๋ ฅ ๊ฒฐ๊ณผ:

1
2
3
4
5
Before function call
Hello!
After function call
my_function
This is my function.

๐Ÿงฉ functools.wraps

  • functools.wraps๋Š” Python์—์„œ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ, ์›๋ž˜ ํ•จ์ˆ˜์˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ(์˜ˆ: ํ•จ์ˆ˜ ์ด๋ฆ„, ๋ฌธ์„œ ๋ฌธ์ž์—ด ๋“ฑ)๋ฅผ ๋ณด์กดํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋Š” ์œ ํ‹ธ๋ฆฌํ‹ฐ์ž…๋‹ˆ๋‹ค.
    • ์ผ๋ฐ˜์ ์œผ๋กœ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์ ์šฉํ•˜๋ฉด ์›๋ž˜ ํ•จ์ˆ˜์˜ ์ด๋ฆ„(name)๊ณผ ๋ฌธ์„œ ๋ฌธ์ž์—ด(doc)์ด ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋กœ ๋Œ€์ฒด๋œ ํ•จ์ˆ˜(์ฃผ๋กœ wrapper ํ•จ์ˆ˜)๋กœ ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค.
    • ์ด๋กœ ์ธํ•ด ๋””๋ฒ„๊น…์ด๋‚˜ ํ•จ์ˆ˜์˜ ๋ฉ”ํƒ€์ •๋ณด๋ฅผ ํ™•์ธํ•˜๊ธฐ ์–ด๋ ค์šด ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ฃผ์š” ๊ธฐ๋Šฅ
    • ์›๋ž˜ ํ•จ์ˆ˜ ์ด๋ฆ„ ์œ ์ง€: ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๊ฐ€ ์ ์šฉ๋œ ํ•จ์ˆ˜๋„ ์›๋ž˜ ํ•จ์ˆ˜ ์ด๋ฆ„์œผ๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.
    • ๋ฌธ์„œ ๋ฌธ์ž์—ด ๋ณด์กด: ์›๋ž˜ ํ•จ์ˆ˜์˜ Docstring์ด ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.
    • ๊ธฐํƒ€ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๋ณด์กด: module, annotations, dict ๊ฐ™์€ ์†์„ฑ๋„ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.

6. ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์ค‘์ฒฉ

๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” ์—ฌ๋Ÿฌ ๊ฐœ๋ฅผ ๋™์‹œ์— ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋•Œ ์ ์šฉ ์ˆœ์„œ๋Š” ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ถ€ํ„ฐ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def decorator1(func):
    def wrapper():
        print("Decorator 1")
        func()
    return wrapper

def decorator2(func):
    def wrapper():
        print("Decorator 2")
        func()
    return wrapper

@decorator1
@decorator2
def hello():
    print("Hello!")

hello()
  • decorator1: ๊ฐ€์žฅ ๋ฐ”๊นฅ์ชฝ์—์„œ ์‹คํ–‰๋˜๋Š” ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์ž…๋‹ˆ๋‹ค.
  • decorator2: ๊ฐ€์žฅ ์•ˆ์ชฝ์—์„œ ์‹คํ–‰๋˜๋Š” ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์ž…๋‹ˆ๋‹ค.
  • hello: ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์ค‘์ฒฉ์„ ํ†ตํ•ด ํ˜ธ์ถœ ์ˆœ์„œ์— ๋”ฐ๋ผ ๋ฉ”์‹œ์ง€๊ฐ€ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.

์ถœ๋ ฅ ๊ฒฐ๊ณผ:

1
2
3
Decorator 1
Decorator 2
Hello!

7. ๊ฒฐ๋ก 

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

๐Ÿ’Œ SUMMARY

  1. ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” ํ•จ์ˆ˜๋ฅผ ๊พธ๋ฉฐ์ฃผ๋Š” ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

  2. @๋ฅผ ์‚ฌ์šฉํ•ด ๊ฐ„๋‹จํ•˜๊ฒŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  3. ๋‹ค์–‘ํ•œ ํ™œ์šฉ๋ฒ•(๋กœ๊น…, ์‹คํ–‰ ์‹œ๊ฐ„ ์ธก์ •, ์ธ์ฆ ๋“ฑ)์œผ๋กœ ์ฝ”๋“œ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  4. functools.wraps๋ฅผ ์‚ฌ์šฉํ•ด ์›๋ž˜ ํ•จ์ˆ˜ ์ •๋ณด๋ฅผ ๋ณด์กดํ•˜์„ธ์š”.

์˜ค๋Š˜์€ ํฌ๋ฆฌ์Šค๋งˆ์Šค ํŠน์ง‘์œผ๋กœ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ๊ฐœ๋…์„ ์ •๋ฆฌํ•ด๋ณด์•˜๋Š”๋ฐ์š”! ์ €๋„ ์‹ค์€ ์ž˜ ์‚ฌ์šฉํ•˜์ง€๋Š” ๋ชปํ•˜๋Š” ํŽธ์ด์ง€๋งŒ ๋งŽ์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋ณด๋ฉด ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์“ฐ๋Š” ์ฝ”๋“œ๋“ค์ด ๋งŽ์•„์„œ ์ค‘์š”ํ•œ ๊ฐœ๋…์ด๋ž๋‹ˆ๋‹ค!! ๐Ÿ˜Š

์ด์ œ ์ง์ ‘ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ๋งŒ๋“ค์–ด ๋ณด๊ณ , ๋‹ค์–‘ํ•œ ํ•จ์ˆ˜์— ์ ์šฉํ•ด ๋ณด์„ธ์š”!

๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์˜ ๋งค๋ ฅ์— ๋น ์ง€๊ฒŒ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์˜ค๋Š˜๋„ ์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค :)



-->