2025.03.12 본_캠프 16일차

민동·2025년 3월 12일
1

본캠프

목록 보기
18/74

1-1 python 변수 스코프(Variable Scope) & 클로저(Closure)

keyword : scope,global,nonlocal,locals,globals,closure

1. 변수 스코프(scope)란?

변수의 유효 범위(scope)는 변수가 어디에 읽고,쓸 수 있는지를 결정하는 개념이다. 파이썬에서는 전역변수(global variable) 와 지역변수(local variable)로 나뉜다.

  • 전역 변수는 변하지 않는 고정 값을 지정할 때 사용
  • 지역 변수는 함수 내부 로직을 해결하는 용도로 사용되며, 함수가 종료되면 사라진다.
전역 변수 (global)지역 변수 (local)
함수 내 읽기가능가능
함수 내 쓰기(global 선언 필요)가능
함수 외 읽기가능불가능
함수 외 쓰기가능불가능

1. 전역 변수 사용

a = 10  # 전역 변수

def foo():
    print('Ex1 >', a)  # 전역 변수 읽기

foo()
print('Ex1 >', a)  # 전역 변수 읽기

전역 변수는 함수 내에서도 읽을 수 있음.


2. 지역 변수와 전역 변수의 차이

b = 20  # 전역 변수

def bar():
    b = 30  # 지역 변수 선언 (전역 변수 b와 다름)
    print('Ex2 >', b)  # 지역 변수 출력

bar()
print('Ex2 >', b)  # 전역 변수 출력 (변하지 않음)

Ex2 >  30
Ex2 >  20

함수 내부의 지역 변수는 함수가 종료되면 사라지며, 전역 변수에는 영향을 주지 않음.


3. 전역 변수 수정 시 오류 발생

c = 40  # 전역 변수

def foobar():
    c += 10  # 전역 변수 c를 수정하려 하면 오류 발생
    print('Ex3 >', c)

foobar()

UnboundLocalError 발생

  • c += 10을 실행하면 파이썬이 c지역 변수로 간주하기 때문에 오류 발생!

    해결 방법: global 키워드 사용


4. global 키워드를 사용하여 전역 변수 수정

d = 50  # 전역 변수

def barfoo():
    global d  # 전역 변수 수정 가능하도록 선언
    d = 60
    print('Ex4 >', d)

barfoo()
print('Ex4 >', d)  # 60 (전역 변수 d가 변경됨)

global 키워드를 사용하면 함수 내부에서 전역 변수 수정 가능.
하지만 전역 변수 수정은 가급적 피하는 것이 좋음


파이썬 클로저(Closure)

클로저(Closure)는 함수 안에서 또 다른 함수를 정의하고, 그 내부 함수를 반환하는 구조를 의미한다.
외부 함수(outer)와 내부 함수(inner)가 서로 공유하는 변수를 함께 사용하며 실행되는 방식이다.

클로저를 사용하는 이유

  1. 변수 낭비를 줄이고 효율적으로 함수 호출
  2. 모듈화된 코드 작성 가능
  3. 변수의 유효 범위를 제한하여 보안성을 높임

5 nonlocal 키워드를 사용한 클로저

def outer():
    e = 70  # 외부 함수의 지역 변수
    def inner():
        nonlocal e  # 외부 함수의 변수 수정 가능
        e += 10
        print('Ex5 >', e)
    return inner

in_test = outer()  # 클로저 생성
in_test()  # Ex5 > 80
in_test()  # Ex5 > 90

nonlocal을 사용하면 외부 함수의 변수를 내부 함수에서 수정 가능
클로저(Closure) 패턴을 활용하여 특정 값 유지 가능


locals()globals()

변수의 범위(scope)를 확인하는 함수.

6 locals() (지역 변수 확인)

def func(var): 
    x = 10 
    def printer():
        print('Printer Func Inner') 
    print('Func Inner', locals())  # 지역 변수 출력

func("Hi")

locals()현재 함수 내의 지역 변수를 딕셔너리 형태로 출력.


7 globals() (전역 변수 확인)

print('Ex7 >', globals())  # 전역 변수 전체 출력
globals()['test_variable'] = 100  # 전역 변수 동적 추가
print('Ex7 >', globals())  # test_variable 추가됨

globals()를 사용하면 전역 변수 목록을 확인하고 동적으로 수정 가능.


8 globals()를 활용한 지역 변수 → 전역 변수 변환

for i in range(1, 10): 
    for k in range(1, 10): 
        globals()['plus_{}_{}'.format(i, k)] = i + k 

print(plus_3_5)  # 8
print(plus_9_9)  # 18

globals()를 사용하면 반복문에서 생성된 변수들을 전역 변수로 저장 가능.


정리

전역 변수(global): 함수 외부에서 선언, 프로그램 전체에서 접근 가능
지역 변수(local): 함수 내부에서 선언, 함수 실행이 끝나면 사라짐
전역 변수 수정 시 오류 발생, 해결 방법:

  • global 키워드 사용 → 전역 변수 수정 가능 (⚠ 지양하는 것이 좋음)
  • nonlocal 키워드 사용 → 클로저 내부에서 외부 변수 수정 가능
    locals()현재 함수 내의 변수 확인
    globals()전역 변수 목록 확인 및 수정 가능

1-2

Python 고급 함수: lambda, map, filter, reduce

Keyword: lambda, map, filter, reduce


lambda 함수란?

익명 함수(Anonymous Function) 로, 한 줄로 간결하게 작성할 수 있는 함수.
일반 def 함수는 메모리에 저장되지만, lambda 함수는 힙(Heap) 영역에서 즉시 소멸됨.

lambda 사용 예시

cul = lambda a, b, c: a * b + c
print('Ex1 >', cul(10, 15, 20))  # (10 * 15) + 20 = 170

def를 사용할 필요 없이 간단한 연산을 수행할 수 있음.


map() 함수란?

map() 함수는 리스트의 각 요소에 특정 함수를 적용하여 새로운 리스트를 생성하는 함수.

map(함수, 반복 가능한 자료형)

map() 기본 사용법

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

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

result = list(map(square, numbers))
print(result)  # [1, 4, 9, 16, 25]

# lambda 사용
result = list(map(lambda x: x ** 2, numbers))
print(result)  # [1, 4, 9, 16, 25]

lambda를 사용하면 코드가 더 간결해짐.


map()을 반환할 때 list()를 꼭 써야 하는 이유?

def also_square(nums):
    def double(x):
        return x ** 2
    return map(double, nums)

print('Ex2 >', also_square([10, 20, 30]))  # <map object at 0x...>
print('Ex2 >', list(also_square([10, 20, 30])))  # [100, 400, 900]

map()제너레이터(generator) 이므로, 리스트로 변환해야 값이 출력됨.
제너레이터는 메모리를 절약하기 위해 한 번에 하나씩 처리되기 때문.


filter() 함수란?

조건을 만족하는 요소만 필터링하여 새로운 리스트를 생성하는 함수.

filter() 기본 사용법

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

# Lambda 사용
result = list(filter(lambda x: x % 2 == 0, numbers))
print(result)  # [2, 4, 6, 8, 10]

filter()의 함수는 True 또는 False 값을 반환해야 하며, True인 요소만 남음.


filter()을 반환할 때 list()를 꼭 써야 하는 이유?

def also_evens(nums):
    def is_even(x):
        return x % 2 == 0
    return filter(is_even, nums)

print('Ex3 >', also_evens([1, 2, 3, 4]))  # <filter object at 0x...>
print('Ex3 >', list(also_evens([1, 2, 3, 4])))  # [2, 4]

filter()map()과 동일하게 제너레이터(generator) 형태로 저장되므로 list()로 변환해야 결과가 출력됨.


reduce() 함수란?

reduce() 함수는 리스트의 모든 요소를 하나의 값으로 축약(누적 연산) 하는 함수.
reduce()를 사용하려면 functools 모듈을 import해야 함.

reduce() 기본 사용법

from functools import reduce

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

# 모든 요소를 더하기
result = reduce(lambda x, y: x + y, numbers)
print(result)  # 15 (1 + 2 + 3 + 4 + 5)

리스트의 모든 요소를 하나로 합칠 때 유용함.


map(), filter(), reduce() 비교

함수동작 방식반환 값
map()각 요소를 변환하여 새로운 리스트 생성[결과1, 결과2, 결과3, ...]
filter()조건을 만족하는 요소만 선택하여 새로운 리스트 생성[True인 요소들]
reduce()모든 요소를 하나의 값으로 축약(누적 연산)단일 값

예제 비교

from functools import reduce

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

# map 사용 (각 요소를 제곱)
squared = list(map(lambda x: x ** 2, numbers))
print(squared)  # [1, 4, 9, 16, 25]

# filter 사용 (짝수만 선택)
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens)  # [2, 4]

# reduce 사용 (모든 요소 합산)
total = reduce(lambda x, y: x + y, numbers)
print(total)  # 15

map()각 요소 변환, filter()조건 필터링, reduce()누적 연산에 사용됨.


정리

  • lambda → 익명 함수, 한 줄로 간단하게 표현 가능
  • map() → 리스트의 각 요소를 변환하여 새로운 리스트 반환
  • filter() → 조건을 만족하는 요소만 필터링하여 새로운 리스트 반환
  • reduce() → 리스트의 모든 요소를 하나의 값으로 축약(누적 연산)
  • map(), filter()제너레이터 객체를 반환하므로 list()로 변환해야 값이 출력됨.

1-3

Python 객체의 종류: Mutable vs Immutable

Keyword: mutable, immutable, list, tuple, copy


1. Mutable vs Immutable 개념

파이썬의 객체는 변경 가능(Mutable) 한 객체와 변경 불가능(Immutable) 한 객체로 나뉜다.

  • Mutable 객체: 객체를 생성한 후에도 내부 값을 변경할 수 있는 객체
  • Immutable 객체: 객체를 생성한 후 내부 값을 변경할 수 없는 객체

2. Mutable과 Immutable 객체 비교

class설명구분
listmutable 한 순서가 있는 객체 집합mutable
setmutable 한 순서가 없는 고유한 객체 집합mutable
dictkey와 value가 매핑된 객체, 순서 없음mutable
bool참, 거짓immutable
int정수immutable
float실수immutable
tupleimmutable 한 순서가 있는 객체 집합immutable
str문자열immutable
frozensetimmutable한 setimmutable

파이썬에서 일반적으로 사용자가 작성하는 클래스는 대부분 mutable 객체이며,
immutable한 클래스를 만들려면 특별한 방법이 필요하다.


3. Mutable 객체 예제 (List)

a = [1, 2, 3]  # 리스트 생성
b = a  # 같은 객체를 참조
b[0] = 100

print(a)  # [100, 2, 3]
print(b)  # [100, 2, 3]
  • 리스트는 mutable 객체이므로 b[0]을 변경하면 a[0]도 함께 변경된다.

4. Immutable 객체 예제 (Tuple)

a = (1, 2, 3)  # 튜플 생성
b = a
b = (100, 2, 3)  # 새로운 튜플 할당

print(a)  # (1, 2, 3)
print(b)  # (100, 2, 3)
  • 튜플은 immutable 객체이므로, 기존 값을 변경할 수 없고 새로운 객체를 할당해야 한다.

5. 객체 복사 개념 (Shallow Copy vs Deep Copy)

단순 복사 (Copy)

a_list = [1, 2, 3, [4, 5, 6], [7, 8, 9]]
b_list = a_list  # 같은 객체를 참조


b_list[2] = 100  # 변경

print(a_list)  # [1, 2, 100, [4, 5, 6], [7, 8, 9]]
print(b_list)  # [1, 2, 100, [4, 5, 6], [7, 8, 9]]
  • 같은 메모리를 참조하므로 b_list를 변경하면 a_list도 함께 변경됨.

얕은 복사 (Shallow Copy)

import copy

c_list = [1, 2, 3, [4, 5, 6], [7, 8, 9]]
d_list = copy.copy(c_list)  # 얕은 복사
d_list[4][2] = 10000

d_list[1] = 100
d_list[3].append(1000)  # 내부 리스트 변경
d_list[4][2] = 10000

print(c_list)  # 원본 리스트도 변경됨
print(d_list)  # 변경된 값 포함

Ex2 >  [1, 2, 3, [4, 5, 6, 1000], [7, 8, 10000]]
Ex2 >  [1, 100, 3, [4, 5, 6, 1000], [7, 8, 10000]]
  • 새로운 객체를 생성하지만 내부 리스트는 원본과 동일한 주소를 공유한다.

깊은 복사 (Deep Copy)

e_list = [1, 2, 3, [4, 5, 6], [7, 8, 9]]
f_list = copy.deepcopy(e_list)  # 깊은 복사

f_list[3].append(1000)  # 내부 리스트 변경
f_list[4][1] =10000


print(e_list)  # 원본 리스트 영향 없음
print(f_list)  # 복사본만 변경됨

Ex3 >  [1, 2, 3, [4, 5, 6], [7, 8, 9]]
Ex3 >  [1, 100, 3, [4, 5, 6], [7, 8, 9]]
  • 깊은 복사는 리스트 내부의 리스트까지 새로운 주소를 가지므로 원본과 완전히 독립적이다.

6. shallow copy vs deep copy 비교

복사 방법새 객체 생성 여부내부 객체(리스트 등) 복사 여부원본과 독립성
단순 복사❌ (같은 객체 참조)❌ (같은 내부 객체 공유)없음 (완전히 동일)
얕은 복사 (copy.copy())⭕ (새 객체 생성)❌ (내부 리스트는 공유)부분적 독립 (내부 객체는 공유됨)
깊은 복사 (copy.deepcopy())⭕ (새 객체 생성)⭕ (내부 리스트까지 새로운 주소로 복사)완전 독립

7. 정리

  • Mutable 객체: list, set, dict (내부 값 변경 가능)
  • Immutable 객체: int, float, str, tuple, frozenset (내부 값 변경 불가능)
  • Shallow Copy: 리스트 내부의 참조 객체는 그대로 복사되어 원본과 연결됨
  • Deep Copy: 내부 요소까지 모두 새로운 객체로 복사되어 원본과 완전히 독립적임

객체의 특성을 잘 이해하고 상황에 맞는 복사 방법을 선택하는 것이 중요하다.

profile
아자아자

0개의 댓글