본문 바로가기
Python

18. 함수 (2) - 스코프와 고급 기능

by 샤나엘 2026. 2. 22.
반응형

함수 (2) - 스코프와 고급 기능

18. 함수 (2) - 스코프와 고급 기능

함수 안에서 만든 변수는 밖에서 쓸 수 없다? 인자를 몇 개든 받을 수 있는 함수? 한 줄짜리 함수? 함수의 심화 기능을 익히면 코드가 한 단계 더 유연해진다.


변수의 스코프 (Scope)

스코프란 변수가 살아있는 범위다. 파이썬에서 변수는 어디서 만들었느냐에 따라 접근 가능한 범위가 달라진다.

지역 변수 (Local Variable)

함수 안에서 만든 변수는 함수 안에서만 사용할 수 있다.

def greet():
    message = "안녕하세요"    # 지역 변수
    print(message)

greet()           # 안녕하세요
print(message)    # NameError! 함수 밖에서 접근 불가

전역 변수 (Global Variable)

함수 밖에서 만든 변수는 어디서든 읽을 수 있다.

name = "김철수"    # 전역 변수

def greet():
    print(f"안녕하세요, {name}님!")    # 전역 변수 읽기 가능

greet()       # 안녕하세요, 김철수님!
print(name)   # 김철수

같은 이름의 변수

함수 안에서 같은 이름의 변수를 만들면, 지역 변수가 우선된다.

x = 10    # 전역 변수

def test():
    x = 99    # 지역 변수 (전역 x와 별개)
    print(f"함수 안: {x}")

test()          # 함수 안: 99
print(f"함수 밖: {x}")   # 함수 밖: 10  (전역 x는 그대로)

 

스코프 다이어그램


global 키워드

함수 안에서 전역 변수를 수정하려면 global 키워드를 사용한다.

count = 0

def increment():
    global count     # 전역 변수 count를 수정하겠다고 선언
    count += 1

increment()
increment()
increment()
print(count)    # 3

⚠️ 주의: global은 가급적 사용하지 않는 것이 좋다. 함수는 매개변수와 return으로 데이터를 주고받는 것이 깔끔하다.

# global 대신 이렇게!
def increment(count):
    return count + 1

count = 0
count = increment(count)    # 1
count = increment(count)    # 2

LEGB 규칙

파이썬은 변수를 찾을 때 LEGB 순서로 탐색한다.

L (Local)     → 함수 내부
E (Enclosing) → 감싸는 함수 (중첩 함수일 때)
G (Global)    → 모듈(파일) 전체
B (Built-in)  → 파이썬 내장
x = "전역"           # Global

def outer():
    x = "외부함수"    # Enclosing

    def inner():
        x = "내부함수"    # Local
        print(x)

    inner()

outer()    # 내부함수

*args - 가변 위치 인자

매개변수 이름 앞에 *를 붙이면, 여러 개의 인자를 튜플로 받을 수 있다.

def add_all(*args):
    print(f"받은 인자: {args}")    # 튜플로 저장됨
    return sum(args)

print(add_all(1, 2, 3))        # 받은 인자: (1, 2, 3) → 6
print(add_all(10, 20, 30, 40)) # 받은 인자: (10, 20, 30, 40) → 100
print(add_all(5))              # 받은 인자: (5,) → 5

일반 매개변수와 함께 사용

def introduce(name, *hobbies):
    print(f"이름: {name}")
    print(f"취미: {', '.join(hobbies)}")

introduce("김철수", "독서", "게임", "요리")
# 이름: 김철수
# 취미: 독서, 게임, 요리

**kwargs - 가변 키워드 인자

매개변수 이름 앞에 **를 붙이면, 키워드 인자를 딕셔너리로 받을 수 있다.

def print_info(**kwargs):
    print(f"받은 인자: {kwargs}")    # 딕셔너리로 저장됨
    for key, value in kwargs.items():
        print(f"  {key}: {value}")

print_info(name="김철수", age=25, city="서울")
# 받은 인자: {'name': '김철수', 'age': 25, 'city': '서울'}
#   name: 김철수
#   age: 25
#   city: 서울

args와 *kwargs 함께 사용

def flex_func(*args, **kwargs):
    print(f"위치 인자: {args}")
    print(f"키워드 인자: {kwargs}")

flex_func(1, 2, 3, name="철수", age=25)
# 위치 인자: (1, 2, 3)
# 키워드 인자: {'name': '철수', 'age': 25}

💡 순서 규칙: 일반 매개변수*args키워드 매개변수**kwargs

def example(a, b, *args, option=True, **kwargs):
    print(a, b, args, option, kwargs)

example(1, 2, 3, 4, option=False, x=10)
# 1 2 (3, 4) False {'x': 10}

 

args와 kwargs 비교


람다 함수 (lambda)

lambda이름 없는 한 줄 함수다. 간단한 함수를 빠르게 만들 때 사용한다.

# 일반 함수
def square(x):
    return x ** 2

# 람다 함수 (같은 기능)
square = lambda x: x ** 2

print(square(5))    # 25

문법

lambda 매개변수: 표현식
# 두 수의 합
add = lambda a, b: a + b
print(add(3, 5))    # 8

# 짝수 판별
is_even = lambda x: x % 2 == 0
print(is_even(4))   # True
print(is_even(7))   # False

# 조건 표현식과 함께
grade = lambda score: "합격" if score >= 60 else "불합격"
print(grade(85))    # 합격
print(grade(45))    # 불합격

💡 람다는 한 줄 표현식만 가능하다. 복잡한 로직은 일반 함수로 작성하자.


map() 함수

map(함수, 반복가능객체) — 모든 요소에 함수를 적용한 결과를 반환한다.

numbers = [1, 2, 3, 4, 5]

# 일반적인 방법
squared = []
for n in numbers:
    squared.append(n ** 2)
print(squared)    # [1, 4, 9, 16, 25]

# map 사용
squared = list(map(lambda x: x ** 2, numbers))
print(squared)    # [1, 4, 9, 16, 25]
# 문자열 리스트를 정수로 변환
str_nums = ["10", "20", "30"]
int_nums = list(map(int, str_nums))
print(int_nums)    # [10, 20, 30]

# 이름에 "님" 붙이기
names = ["김철수", "이영희", "박민수"]
result = list(map(lambda name: name + "님", names))
print(result)    # ['김철수님', '이영희님', '박민수님']

filter() 함수

filter(함수, 반복가능객체) — 함수가 True를 반환하는 요소만 걸러낸다.

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# 짝수만 필터링
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens)    # [2, 4, 6, 8, 10]

# 80점 이상만 필터링
scores = [65, 92, 78, 88, 55, 95, 42]
passed = list(filter(lambda s: s >= 80, scores))
print(passed)    # [92, 88, 95]

map과 filter 조합

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# 짝수만 골라서 제곱
result = list(map(lambda x: x ** 2, filter(lambda x: x % 2 == 0, numbers)))
print(result)    # [4, 16, 36, 64, 100]

# 리스트 컴프리헨션으로 같은 결과 (더 읽기 쉬움)
result = [x ** 2 for x in numbers if x % 2 == 0]
print(result)    # [4, 16, 36, 64, 100]

sorted()와 lambda

sorted()key 매개변수에 람다를 사용하면 정렬 기준을 자유롭게 설정할 수 있다.

# 절대값 기준 정렬
numbers = [-5, 3, -1, 4, -2]
result = sorted(numbers, key=lambda x: abs(x))
print(result)    # [-1, -2, 3, 4, -5]

# 문자열 길이 기준 정렬
words = ["banana", "apple", "cherry", "date"]
result = sorted(words, key=lambda w: len(w))
print(result)    # ['date', 'apple', 'banana', 'cherry']

# 딕셔너리 리스트를 특정 키로 정렬
students = [
    {"name": "김철수", "score": 85},
    {"name": "이영희", "score": 92},
    {"name": "박민수", "score": 78},
]
result = sorted(students, key=lambda s: s["score"], reverse=True)
for s in result:
    print(f"{s['name']}: {s['score']}점")
# 이영희: 92점
# 김철수: 85점
# 박민수: 78점

실전 예시: 학생 데이터 처리

students = [
    {"name": "김철수", "scores": [85, 90, 78]},
    {"name": "이영희", "scores": [92, 88, 95]},
    {"name": "박민수", "scores": [70, 65, 80]},
    {"name": "최지원", "scores": [88, 92, 90]},
]

def get_average(*scores):
    """여러 점수의 평균 계산"""
    return sum(scores) / len(scores)

def process_students(students, min_avg=80):
    """학생 데이터 처리: 평균 계산 + 합격자 필터링"""
    # 평균 추가
    for s in students:
        s["avg"] = get_average(*s["scores"])

    # 합격자 필터링
    passed = list(filter(lambda s: s["avg"] >= min_avg, students))

    # 평균 높은 순 정렬
    passed.sort(key=lambda s: s["avg"], reverse=True)

    return passed

result = process_students(students)
for s in result:
    print(f"{s['name']}: 평균 {s['avg']:.1f}점")
# 이영희: 평균 91.7점
# 최지원: 평균 90.0점
# 김철수: 평균 84.3점

직접 해보기

문제 1. 전역 변수 total을 0으로 초기화하고, 숫자 리스트를 받아 total에 합계를 누적하는 add_to_total(numbers) 함수를 만들어보자.

문제 2. *args를 사용해서 받은 모든 숫자 중 최대값과 최소값을 튜플로 반환하는 min_max(*args) 함수를 만들어보자.

문제 3. **kwargs를 사용해서 학생 정보를 "키: 값" 형태로 출력하는 print_student(**kwargs) 함수를 만들어보자.

문제 4. map()lambda를 사용해서 리스트 [1, 2, 3, 4, 5]의 각 요소를 세제곱한 리스트를 만들어보자.

문제 5. filter()lambda를 사용해서 문자열 리스트에서 길이가 3 이하인 것만 걸러내보자.

정답 보기
# 문제 1
total = 0
‍
def add_to_total(numbers):
    global total
    total += sum(numbers)
‍
add_to_total([10, 20, 30])
add_to_total([5, 15])
print(total)    # 80
‍
# 문제 2
def min_max(*args):
    return min(args), max(args)
‍
print(min_max(3, 7, 1, 9, 2))    # (1, 9)
print(min_max(100, 50))           # (50, 100)
‍
# 문제 3
def print_student(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")
‍
print_student(이름="김철수", 나이=20, 학과="컴퓨터공학")
# 이름: 김철수
# 나이: 20
# 학과: 컴퓨터공학
‍
# 문제 4
cubed = list(map(lambda x: x ** 3, [1, 2, 3, 4, 5]))
print(cubed)    # [1, 8, 27, 64, 125]
‍
# 문제 5
words = ["hi", "hello", "yes", "no", "python", "ok"]
short = list(filter(lambda w: len(w) <= 3, words))
print(short)    # ['hi', 'yes', 'no', 'ok']

오늘의 정리

항목 내용
지역 변수 함수 안에서 만든 변수. 함수 밖에서 접근 불가
전역 변수 함수 밖에서 만든 변수. 어디서든 읽기 가능
global 함수 안에서 전역 변수를 수정할 때 사용
*args 여러 위치 인자를 튜플로 받음
**kwargs 여러 키워드 인자를 딕셔너리로 받음
lambda lambda 매개변수: 표현식 — 한 줄 익명 함수
map() 모든 요소에 함수 적용. list(map(함수, 리스트))
filter() 조건에 맞는 요소만 걸러냄. list(filter(함수, 리스트))

다음 편 예고: 재귀 함수 - 자기 자신을 호출하는 함수

함수가 자기 자신을 호출한다고? 팩토리얼, 피보나치 수열, 하노이 탑까지 — 재귀 함수의 마법 같은 세계를 탐험해보자.


태그: 파이썬 Python 파이썬독학 함수 스코프 args kwargs 람다함수 lambda 파이썬기초 IT교육

반응형

댓글