클로저는 외부 함수의 변수를 내부 함수가 참조하고 있을 때, 외부 함수가 종료되어도 해당 변수에 접근할 수 있는 함수이다.
➡️ 즉, 내부 함수가 외부 함수의 스코프에 있는 변수들을 기억하고 있는 상태를 말한다.
def outer(out1):
def inner(in1):
print("inner call")
print("outer argument: ", in1)
out1() # 여기서 out1은 hello 함수
return inner
def hello():
print("hello")
f = outer(hello)
f(10)
inner call
outer argument: 10
hello
실행 순서
f = outer(hello) 호출됨outer(hello)가 호출되면서 hello 함수 자체가 out1으로 전달됨.inner(in1)이라는 내부 함수를 정의하고, 이 함수를 리턴함.즉, f에는 inner 함수가 저장됨.
👉 f = inner가 된 것임.
f(10) → 즉 inner(10) 실행됨inner 함수가 호출되며 in1 = 10이 전달됨.print("inner call") → 출력: inner callprint("outer argument: ", in1) → 출력: outer argument: 10out1() 호출됨 → out1은 처음에 넘겨준 hello 함수이므로 hello() 호출hello()는 print("hello") 실행 → 출력: helloouter() 함수가 끝나도 x = 10을 기억하고 있다.
이 x는 inner() 함수 내부에서 계속 사용할 수 있다.
데코레이터는 기존 함수에 기능을 추가할 때 사용하는 문법입니다.
클로저 개념을 활용해서, 함수 실행 전후에 다른 기능을 넣을 수 있어요.
def decorator(func):
def wrapper():
print("함수 실행 전")
func()
print("함수 실행 후")
return wrapper
@decorator # say_hello = decorator(say_hello)
def say_hello():
print("안녕하세요!")
say_hello()
함수 실행 전
안녕하세요!
함수 실행 후
@decorator는 say_hello() 함수를 decorator() 함수에 넣어 새로운 함수로 바꿔주는 문법이에요.
➡️ 즉, say_hello = decorator(say_hello) 와 동일합니다.
데코레이터 안의 wrapper()가 클로저처럼 func를 기억하고 있기 때문에 작동하는 거예요.
✅ 요약
| 개념 | 클로저 | 데코레이터 |
|---|---|---|
| 핵심 기능 | 외부 변수 기억 | 함수에 기능 추가 |
| 필요한 구조 | 함수 안의 함수 | 함수 안의 함수 (클로저 활용) |
| 주요 사용처 | 상태 유지 | 로깅, 권한 확인, 실행 시간 측정 등 |
# 실행 시간 측정 데코레이터
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"실행 시간: {end - start:.4f}초")
return result
return wrapper
@timer
def slow_function():
time.sleep(2)
print("실행 완료")
slow_function()
| 레벨 | 의미 | 로깅 함수 |
|---|---|---|
| DEBUG | 디버깅 용도로 사용 (가장 낮음) | debug() |
| INFO | 일반 정보 출력용 | info() |
| WARNING | 경고, 문제 될 가능성 있음 | warning() |
| ERROR | 에러 발생 | error() |
| CRITICAL | 치명적인 문제 (프로그램 중단 가능) | critical() |
프로세스: 운영체제가 프로그램 실행을 위해 제공하는 독립된 실행 공간이다
스레드: 프로세스 내에서 실행되는 작업의 단위로, 여러 스레드가 하나의 프로세스 자원을 공유하며 동시에 실행될 수 있다.
스레드는 프로세스 내에서 실행되는 작업 단위로, 멀티스레딩을 통해 하나의 프로그램이 여러 작업을 동시에 수행할 수 있다.
운영체제는 스레드를 스케줄링하여 CPU 자원을 효율적으로 분배한다..
데몬 스레드: 메인 스레드가 종료되면 자동으로 종료되는 스레드이다
서브 스레드를 데몬으로 설정하려면 daemon 속성을 True로 설정한다.
Fork: 메인 스레드에서 새로운 서브 스레드를 생성하는 과정이다.
Join: 메인 스레드가 서브 스레드의 작업이 완료될 때까지 기다리는 메서드이다.
파이썬 스크립트를 실행하면 기본적으로 하나의 메인 프로세스가 생성된다.
multiprocessing.current_process() 함수를 사용하여 현재 프로세스의 이름과 PID(Process ID)를 확인할 수 있다.
import multiprocessing as mp
if __name__ == "__main__":
proc = mp.current_process()
print(proc.name) # 프로세스 이름
print(proc.pid) # 프로세스 ID
부모 프로세스가 자식 프로세스를 생성하는 과정을 스포닝이라고 한다.
multiprocessing.Process 클래스를 사용하여 새로운 프로세스를 생성하고, start() 메서드를 호출하여 실행한다.
import multiprocessing as mp
import time
def worker():
proc = mp.current_process()
print(proc.name)
print(proc.pid)
time.sleep(5)
print("SubProcess End")
if __name__ == "__main__":
proc = mp.current_process()
print(proc.name)
print(proc.pid)
p = mp.Process(name="SubProcess", target=worker)
p.start()
print("MainProcess End")
더 정확한 코드는 "git hub"