[AI Assignment - Python] π λ°μ½λ μ΄ν° μμ μ 볡 β timer, retry, validate_types μ§μ ꡬν
π― κ³Όμ : μ€λ¬΄ν λ°μ½λ μ΄ν° 3κ° κ΅¬ν #
@timerβ ν¨μ μ€ν μκ° μΈ‘μ @retry(max_attempts=3)β μ€ν¨ μ μλ μ¬μλ@validate_typesβ νμ ννΈ κΈ°λ° λ°νμ νμ κ²μ¦
λͺ¨λ λ°μ½λ μ΄ν°μ functools.wrapsλ₯Ό μ μ©νλ κ²μ΄ μꡬμ¬νμ΄μμ΅λλ€.
π λ°μ½λ μ΄ν° κΈ°λ³Έ ꡬ쑰 #
κ³Όμ μ λ€μ΄κ°κΈ° μ μ λ¨Όμ κΈ°λ³Έ ꡬ쑰λ₯Ό μ 리νμ΅λλ€.
1def my_decorator(func):
2 def wrapper(*args, **kwargs):
3 # ν¨μ μ€ν μ μ ν μΌ
4 result = func(*args, **kwargs)
5 # ν¨μ μ€ν νμ ν μΌ
6 return result
7 return wrapper
8
9# @my_decoratorλ μ¬μ€ μ΄κ²κ³Ό κ°μ΅λλ€:
10# say_hello = my_decorator(say_hello)μΈμλ₯Ό λ°λ λ°μ½λ μ΄ν°λ ν κ²Ή λ κ°μλλ€:
1def repeat(n: int): # β λ°μ½λ μ΄ν° ν©ν 리
2 def decorator(func): # β μ€μ λ°μ½λ μ΄ν°
3 def wrapper(*args, **kwargs):
4 ...
5 return wrapper
6 return decorator1οΈβ£ @timer #
1μ°¨ μλ #
1def timer(func):
2 def running_check(*args, **kwargs):
3 start_time = time.time()
4 result = func(*args, **kwargs)
5 end_time = time.time()
6 if result == 'done':
7 text = f"μΆλ ₯: slow_function μ€ν μκ°: {end_time - start_time:.2f}μ΄"
8 return text
9 print(running_check().__repr__())
10 return running_checkμ§μ λ°μ μ 3κ°μ§ #
if result == 'done' νλμ½λ©: λ°μ½λ μ΄ν°λ μ΄λ€ ν¨μλ κ°μ μ μμ΄μΌ ν©λλ€. 'done'μ λ°ννλ ν¨μμμλ§ μλνλ©΄ λ²μ©μ±μ΄ μμ΅λλ€.
textλ₯Ό λ°ννκ³ resultλ₯Ό μμ€: μλ ν¨μμ λ°νκ°μ λλ €μ€μΌ νλλ°, μΆλ ₯ λ¬Έμμ΄μ λ°ννκ³ μμμ΅λλ€. ifλ₯Ό νμ§ μμΌλ©΄ textκ° λ―Έμ μλμ΄ NameErrorλ λ°μν©λλ€.
λ°μ½λ μ΄ν° μμμ μ§μ μ€ν: print(running_check()) μ΄ μ€ λλ¬Έμ @timerκ° λΆλ μκ° ν¨μκ° λ°λ‘ μ€νλ©λλ€. λ°μ½λ μ΄ν°λ κ°μΌ ν¨μλ₯Ό λ°νλ§ ν΄μΌ ν©λλ€.
1# μ€ν νμ΄λ° μ΄ν΄
2@timer # β μ΄ μμ μ timer(slow_function) μ€ν
3def slow_function(): # β wrapperλ₯Ό λ°νλ§ ν΄μΌ ν¨
4 ...
5
6slow_function() # β μ΄ μμ μ wrapper() μ€ν2μ°¨ μλ (μμ±) #
1def timer(func):
2 @wraps(func)
3 def wrapper(*args, **kwargs):
4 start = time.time()
5 result = func(*args, **kwargs)
6 elapsed = time.time() - start
7 print(f"{func.__name__} μ€ν μκ°: {elapsed:.2f}μ΄")
8 return result
9 return wrapperTip: ν΅μ¬ ν¨ν΄ β wrapperλ
resultλ₯Ό λ°ν, λ°μ½λ μ΄ν°λwrapperλ₯Ό λ°νν©λλ€.
2οΈβ£ @retry #
1μ°¨ μλ #
1def retry(max_attempts: int):
2 def decorator(func):
3 def wrapper(*args, **kwargs):
4 for _ in range(max_attempts):
5 result = func(*args, **kwargs)
6 return result
7 return wrapper
8 return decoratorμ¬μλ λ‘μ§μ λ°μ½λ μ΄ν° μμ΄ μλλΌ __main__μ λ°λ‘ μμ±νμ΅λλ€:
1# __main__μμ μ§μ μ¬μλ β λ°μ½λ μ΄ν°μ μλ―Έκ° μμ΄μ§
2for i in range(max_attempts):
3 try:
4 check = unstable_api_call()
5 if check['status'] == 'ok':
6 break
7 except Exception as e:
8 print(f"μλ {i+1}/{max_attempts} μ€ν¨: {e}")β οΈ λ°μ½λ μ΄ν°μ κ°μΉλ νΈμΆνλ μͺ½μ΄ μ¬μλ λ‘μ§μ λͺ°λΌλ λκ² λ§λλ κ²μ λλ€.
try/except+for루νκ°wrapperμμ μμ΄μΌ ν©λλ€.
2μ°¨ μλ β return vs raise λ¬Έμ #
1def wrapper(*args, **kwargs):
2 last_exception = None
3 for i in range(max_attempts):
4 try:
5 result = func(*args, **kwargs)
6 print(f"{func.__name__} μλ {i+1}/{max_attempts} μ±κ³΅")
7 return result
8 except Exception as e:
9 last_exception = e
10 print(f"{func.__name__} μλ {i+1}/{max_attempts} μ€ν¨: {e}")
11 return last_exception # β λ¬Έμ !3λ² λ€ μ€ν¨νλ©΄:
result = μλ² μλ΅ μμ
type = <class 'ConnectionError'> β μμΈ κ°μ²΄κ° μ μ λ°νκ°μ²λΌ λμμ΄return last_exceptionμ μμΈλ₯Ό μ μ λ°νκ°μΌλ‘ λλ €λ³΄λ
λλ€. νΈμΆνλ μͺ½μμ μλ¬κ° λ°μνλμ§ μ μκ° μμ΅λλ€.
3μ°¨ μλ β λ°κΉ₯ try/except μΆκ° (μμ μ€ν¨) #
1def wrapper(*args, **kwargs):
2 last_exception = None
3 try:
4 for i in range(max_attempts):
5 try:
6 result = func(*args, **kwargs)
7 return result
8 except Exception as e:
9 last_exception = e
10 break
11 except Exception as e:
12 raise last_exceptionμμͺ½ exceptκ° μ΄λ―Έ μμΈλ₯Ό μ²λ¦¬νκΈ° λλ¬Έμ λ°κΉ₯ exceptκΉμ§ μμΈκ° μ νλμ§ μμμ΅λλ€. κ²°κ³Όμ μΌλ‘ Noneμ΄ λ°νλμμ΅λλ€.
4μ°¨ μλ (μμ±) #
1def retry(max_attempts: int):
2 def decorator(func):
3 @wraps(func)
4 def wrapper(*args, **kwargs):
5 last_exception = None
6 for i in range(max_attempts):
7 try:
8 result = func(*args, **kwargs)
9 print(f"{func.__name__} μλ {i+1}/{max_attempts} μ±κ³΅")
10 return result
11 except Exception as e:
12 last_exception = e
13 print(f"{func.__name__} μλ {i+1}/{max_attempts} μ€ν¨: {e}")
14 raise last_exception
15 return wrapper
16 return decoratorTip: μ±κ³΅νλ©΄
returnμΌλ‘ ν¨μκ° μ¦μ μ’ λ£λ©λλ€.forλ¬Έμ΄ λκΉμ§ λ€ λμλ€λ κ² μμ²΄κ° μ λΆ μ€ν¨λ₯Ό μλ―Ένλ―λ‘,forλ¬Έ λ°λ‘ λ€μraiseλ§ λμΌλ©΄ λ©λλ€.
3οΈβ£ @validate_types #
1μ°¨ μλ β int νλμ½λ© #
1def validate_types(func):
2 @wraps(func)
3 def wrapper(*args, **kwargs):
4 for arg in args:
5 if not isinstance(arg, int):
6 raise TypeError(f"μΈμλ μ μ(int)μ¬μΌ ν©λλ€. λ°μ νμ
: {type(arg)}")
7 return func(*args, **kwargs)
8 return wrapperλμμ νμ§λ§, λͺ¨λ μΈμκ° intμΈμ§λ§ κ²μ¬ν©λλ€. get_type_hintsμ inspectλ₯Ό import ν΄λκ³ λ μ μ°κ³ μμμ΅λλ€. μ΄λ¬λ©΄ greet(name: str, count: int) κ°μ ν¨μμμ strμ λ£μ΄λ μλ¬κ° λ©λλ€.
2μ°¨ μλ β μλ¬ λ©μμ§ λ¬Έμ #
1for param_name, value in zip(params, args):
2 if not isinstance(value, hints[param_name]):
3 raise TypeError(f"μΈμλ μ μ(int)μ¬μΌ ν©λλ€. λ°μ νμ
: {type(param_name)}")λ‘μ§μ λ§μμ§λ§ μλ¬ λ©μμ§μ λ κ°μ§ λ¬Έμ κ° μμμ΅λλ€:
"μ μ(int)"κ° νλμ½λ© βstrμ΄μ΄μΌ νλ νλΌλ―Έν°μλ “μ μ(int)μ¬μΌ ν©λλ€"κ° μΆλ ₯λ¨type(param_name)βparam_nameμ"a"κ°μ λ¬Έμμ΄μ΄λ―λ‘ νμ<class 'str'>μ΄ λμ΄.type(value)λ₯Ό μ¨μΌ ν¨
3μ°¨ μλ (μμ±) #
1def validate_types(func):
2 @wraps(func)
3 def wrapper(*args, **kwargs):
4 hints = get_type_hints(func)
5 sig = inspect.signature(func)
6 params = list(sig.parameters.keys())
7
8 for param_name, value in zip(params, args):
9 if not isinstance(value, hints[param_name]):
10 raise TypeError(
11 f"'{param_name}'μ νμ
μ΄ {hints[param_name].__name__}μ¬μΌ νμ§λ§ "
12 f"{type(value).__name__}μ΄ μ λ¬λ¨"
13 )
14 return func(*args, **kwargs)
15 return wrapperβ μ€ν κ²°κ³Ό #
=== timer ===
slow_function μ€ν μκ°: 1.50μ΄
=== retry ===
unstable_api_call μλ 1/3 μ€ν¨: μλ² μλ΅ μμ
unstable_api_call μλ 2/3 μ±κ³΅
=== validate_types ===
add(1, 2) β 3
add("1", "2") β TypeError: 'a'μ νμ
μ΄ intμ¬μΌ νμ§λ§ strμ΄ μ λ¬λ¨
greet(123, 3) β TypeError: 'name'μ νμ
μ΄ strμ¬μΌ νμ§λ§ intμ΄ μ λ¬λ¨
greet("hello", 3) β hellohellohelloπ functools.wrapsλ μ νμνκ° #
μ²μμλ @wraps μμ΄λ func.__name__μ΄ μ λμμ νμ μλ€κ³ μκ°νμ΅λλ€. νμ§λ§ 그건 λ°μ½λ μ΄ν° λ΄λΆμμ funcμ μ§μ μ°Έμ‘°νκ³ μκΈ° λλ¬Έμ΄μμ΅λλ€. λ¬Έμ λ λ°κΉ₯μμ λ³Ό λμ
λλ€:
1@timer
2def slow_function():
3 """λλ¦° ν¨μμ
λλ€"""
4 ...
5
6# @wraps μμ΄
7print(slow_function.__name__) # β "wrapper" β μλ μ΄λ¦ μμ€
8print(slow_function.__doc__) # β None β docstring μμ€
9
10# @wraps μμΌλ©΄
11print(slow_function.__name__) # β "slow_function"
12print(slow_function.__doc__) # β "λλ¦° ν¨μμ
λλ€"β οΈ FastAPI κ°μ νλ μμν¬κ° ν¨μμ
__name__κ³Ό__doc__μ μ½μ΄μ API λ¬Έμλ₯Ό μλ μμ±νλλ°,@wrapsκ° μμΌλ©΄ λͺ¨λ μλν¬μΈνΈ μ΄λ¦μ΄ “wrapper"λ‘ νμλ©λλ€.
π‘ Python νμ ννΈλ λ°νμμ κ°μ λμ§ μλλ€ #
κ³Όμ μ€μ “νμ
ννΈλ₯Ό λ£μΌλ©΄ add("1", 2)μμ λ°λ‘ μλ¬κ° λμ§ μλμ?“λΌλ μλ¬Έμ΄ μκ²Όμ΅λλ€. λ΅μ μλλλ€.
1def add(a: int, b: int) -> int:
2 return a + b
3
4add("hello", "world") # β "helloworld" (μλ¬ μμ!)
5add(1.5, 2.3) # β 3.8 (int μλλ° μλ¬ μμ!)Pythonμ νμ
ννΈλ κ·Έλ₯ “λ©λͺ¨"μ
λλ€. μ€μ λ‘ νμ
μ κ°μ νλ €λ©΄ mypy κ°μ μ μ λΆμ λꡬλ₯Ό μ°κ±°λ, μ΄λ² κ³Όμ μ @validate_typesμ²λΌ λ°νμμ μ§μ 체ν¬ν΄μΌ ν©λλ€. FastAPIκ° λ΄λΆμ μΌλ‘ μ΄λ° λ°νμ κ²μ¦μ ν΄μ£Όλ λνμ μΈ μμ
λλ€.
π€ λ°μ½λ μ΄ν°κ° μ νμνκ° #
κ³Όμ λ₯Ό νλ©΄μ “κ΅³μ΄ λ°μ½λ μ΄ν°κ° νμνκ°?“λΌλ μλ¬Έμ΄ λ€μμ΅λλ€. νμ§λ§ @retry μμ΄ API νΈμΆ 3κ°λ₯Ό λ§λ λ€κ³ κ°μ νλ©΄ λ΅μ΄ λμ΅λλ€:
1# λ°μ½λ μ΄ν° μμ΄ β μ¬μλ λ‘μ§μ΄ λ§€λ² λ°λ³΅
2def call_openai():
3 for i in range(3):
4 try: return openai.chat(...)
5 except: ...
6
7def call_anthropic():
8 for i in range(3): # β λκ°μ μ½λ
9 try: return anthropic.messages(...)
10 except: ...1# λ°μ½λ μ΄ν°λ‘ β λΉμ¦λμ€ λ‘μ§λ§ λ¨μ
2@retry(max_attempts=3)
3def call_openai():
4 return openai.chat(...)
5
6@retry(max_attempts=3)
7def call_anthropic():
8 return anthropic.messages(...)λ°μ½λ μ΄ν°μ κ°μΉλ ν‘λ¨ κ΄μ¬μ¬μ λΆλ¦¬μ
λλ€. λ‘κΉ
, μΈμ¦, μ¬μλ, μΊμ±, νμ
κ²μ¦ κ°μ 건 λΉμ¦λμ€ λ‘μ§μ΄ μλλ° μ¬κΈ°μ κΈ°μ νμν©λλ€. μ΄κ±Έ ν¨μλ§λ€ λ°λ³΅νλ λμ @ ν μ€λ‘ λΆμ΄λ κ²μ
λλ€.
π μ΄λ² κ³Όμ μμ λ°°μ΄ κ² #
λ°μ½λ μ΄ν° κΈ°λ³Έ ν¨ν΄: wrapperλ resultλ₯Ό λ°ν, λ°μ½λ μ΄ν°λ wrapperλ₯Ό λ°νν©λλ€. λ°μ½λ μ΄ν° μμμ ν¨μλ₯Ό μ§μ μ€ννλ©΄ μ λ©λλ€.
μΈμ μλ λ°μ½λ μ΄ν°: ν©ν 리 β λ°μ½λ μ΄ν° β wrapperμ 3μ€ μ€μ²© ꡬ쑰μ λλ€. ν€μλ μΈμλ‘ νΈμΆν λλ μ΄λ¦μ΄ μΌμΉν΄μΌ νκ³ , μμΉ μΈμλ‘ νΈμΆνλ©΄ μκ΄μμ΅λλ€.
return vs raise: μμΈλ₯Ό returnνλ©΄ μ μ λ°νκ°μ²λΌ λμκ°λλ€. μμΈλ λ°λμ raiseλ‘ λ°μμμΌμΌ νΈμΆνλ μͺ½μμ μΈμ§ν μ μμ΅λλ€.
@wrapsμ μν : μλ ν¨μμ __name__, __doc__ λ± λ©νλ°μ΄ν°λ₯Ό 보쑴ν©λλ€. FastAPI κ°μ νλ μμν¬μ μλ λ¬Έμ μμ±μ μν₯μ μ€λλ€.
νμ
ννΈλ λ©λͺ¨μΌ λΏ: Pythonμ λ°νμμ νμ
μ κ°μ νμ§ μμ΅λλ€. get_type_hints()μ inspect.signature()λ‘ μ§μ μ½μ΄μμΌ ν©λλ€.