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