소개
본 실습에서는 Python 의 함수에 대한 이해를 반환 값 (return values) 과 변수 범위 (variable scope) 개념을 탐구하며 심화시킬 것입니다. 먼저 명시적으로 값을 반환하지 않는 함수들을 검토하며, 이러한 함수들이 어떻게 별도로 사용될 수 있는 결과를 생성하지 않고 동작을 수행하는지 관찰하게 됩니다.
이어서, 함수에 반환 값을 추가하여 프로그램 내에서 캡처하고 활용할 수 있는 출력을 생성하는 방법을 학습합니다. 그런 다음, 지역 변수 (local variables) 와 전역 변수 (global variables) 를 구별하고, 각 변수의 범위와 이것이 함수 내부 및 외부에서 변수 접근성에 어떻게 영향을 미치는지 이해하도록 안내할 것입니다. 또한, global 키워드를 사용하여 함수 내부에서 전역 변수를 수정하는 방법과 중첩 함수 (nested functions) 에서 nonlocal 변수의 개념을 탐구하게 됩니다.
반환 값이 없는 함수 탐색
이 단계에서는 명시적인 return 문이 없는 함수를 탐색합니다. 이러한 함수는 콘솔에 출력하는 것과 같은 동작을 수행하지만, 호출한 코드 부분으로 값을 되돌려주지는 않습니다.
먼저, WebIDE 왼쪽의 파일 탐색기에서 no_return_function.py 파일을 찾습니다. 이 파일을 두 번 클릭하여 편집기에서 엽니다.
no_return_function.py 파일에 다음 코드를 추가합니다.
def greet():
print("Hello, welcome to the world of functions!")
## 함수 호출
greet()
이 스크립트를 실행하려면 상단 메뉴에서 Terminal > New Terminal을 클릭하여 통합 터미널을 엽니다. 그런 다음 다음 명령을 실행합니다.
python ~/project/no_return_function.py
함수의 출력이 콘솔에 인쇄되는 것을 볼 수 있습니다.
Hello, welcome to the world of functions!
greet() 함수는 print() 문을 실행하지만, 변수에 저장할 수 있는 값을 생성하지는 않습니다. 결과를 캡처하려고 할 때 어떤 일이 발생하는지 확인하기 위해 no_return_function.py 파일을 수정합니다. 기존 내용을 다음 코드로 대체합니다.
def greet():
print("Hello, welcome to the world of functions!")
## 함수를 호출하고 그 결과를 변수에 할당
result = greet()
## result 변수의 값 출력
print(f"The result of calling greet() is: {result}")
파일을 저장하고 터미널에서 다시 실행합니다.
python ~/project/no_return_function.py
이제 출력은 다음과 같습니다.
Hello, welcome to the world of functions!
The result of calling greet() is: None
보시다시피 result 변수의 값은 None입니다. Python 에서 명시적인 return 문이 없는 모든 함수는 자동으로 None을 반환합니다. None은 값이 없음을 나타내는 특별한 값입니다. 이는 이러한 함수가 데이터를 생성하기보다는 부수 효과 (예: 출력) 를 위해 사용됨을 확인시켜 줍니다.
함수에 반환 값 추가하기
이전 단계와 달리, 대부분의 함수는 값을 계산하여 호출자에게 반환하도록 설계됩니다. 이는 return 키워드를 사용하여 수행됩니다. return 문은 즉시 함수를 종료하고 지정된 값을 되돌려 보냅니다.
WebIDE 의 파일 탐색기에서 return_function.py 파일을 엽니다.
두 숫자를 더하고 그 합계를 반환하는 함수를 정의하기 위해 다음 코드를 추가합니다.
def add_numbers(a, b):
"""This function adds two numbers and returns the sum."""
sum_result = a + b
return sum_result
## 함수를 호출하고 반환된 값을 저장
total = add_numbers(5, 3)
## 반환된 값 출력
print(f"The sum is: {total}")
## 반환된 값을 다른 연산에 사용
another_total = add_numbers(10, 20) * 2
print(f"Another calculated value: {another_total}")
파일을 저장하고 터미널에서 스크립트를 실행합니다.
python ~/project/return_function.py
반환된 값이 성공적으로 캡처되어 사용되었음을 보여주는 다음 출력을 보게 될 것입니다.
The sum is: 8
Another calculated value: 60
함수는 숫자, 문자열, 리스트, 심지어 튜플 (tuple) 을 포함하여 모든 Python 객체를 반환할 수 있습니다. 튜플을 반환하는 것은 한 번에 여러 값을 반환하는 일반적인 방법입니다.
이것을 실제로 확인하기 위해 return_function.py 파일 끝에 다음 코드를 추가합니다.
def get_user_info():
"""This function returns user information as a tuple."""
name = "labex"
age = 25
city = "Virtual City"
return name, age, city
## 함수를 호출하고 반환된 튜플을 개별 변수로 언패킹 (unpack)
user_name, user_age, user_city = get_user_info()
## 언패킹된 값 출력
print(f"Name: {user_name}, Age: {user_age}, City: {user_city}")
파일을 저장하고 다시 실행합니다.
python ~/project/return_function.py
전체 출력은 이제 다음과 같습니다.
The sum is: 8
Another calculated value: 60
Name: labex, Age: 25, City: Virtual City
여기서 get_user_info()는 튜플로 패키징된 세 개의 값을 반환합니다. 호출하는 코드는 이 튜플을 세 개의 별도 변수로 언패킹하여 여러 반환 값을 쉽게 처리할 수 있도록 합니다.
지역 변수와 전역 변수 구분하기
버그 없는 코드를 작성하려면 변수 범위 (scope) 를 이해하는 것이 필수적입니다. 범위는 프로그램의 어느 위치에서 변수에 접근할 수 있는지를 결정합니다.
- 전역 범위 (Global Scope): 함수 외부에 정의된 변수는 전역 변수입니다. 스크립트의 어디에서든 접근할 수 있습니다.
- 지역 범위 (Local Scope): 함수 내부에 정의된 변수는 지역 변수입니다. 해당 함수 내에서만 접근할 수 있습니다.
이 개념을 탐색해 봅시다. WebIDE 에서 variable_scope.py 파일을 엽니다.
파일에 다음 코드를 추가합니다.
## Global variable
global_variable = "I am a global variable"
def my_function():
## Local variable
local_variable = "I am a local variable"
print(f"Inside the function:")
print(f" Accessing global_variable: {global_variable}")
print(f" Accessing local_variable: {local_variable}")
## 함수 호출
my_function()
## 함수 외부에서 변수에 접근 시도
print(f"\nOutside the function:")
print(f" Accessing global_variable: {global_variable}")
## 여기서 local_variable 에 접근을 시도하면 NameError 가 발생합니다.
## print(f" Accessing local_variable: {local_variable}")
파일을 저장하고 터미널에서 실행합니다.
python ~/project/variable_scope.py
출력은 각 변수의 접근 가능성을 보여줍니다.
Inside the function:
Accessing global_variable: I am a global variable
Accessing local_variable: I am a local variable
Outside the function:
Accessing global_variable: I am a global variable
전역 변수는 어디서든 접근 가능하지만, 지역 변수는 해당 함수 내로 제한됩니다. 마지막 줄의 주석 처리를 해제하면 스크립트는 NameError로 실패합니다.
그렇다면 지역 변수와 전역 변수의 이름이 같으면 어떻게 될까요? 지역 변수는 전역 변수를 "가립니다 (shadows)". 이는 함수 내에서 해당 이름이 지역 변수를 참조한다는 의미입니다.
이러한 섀도잉 (shadowing) 효과를 관찰하기 위해 variable_scope.py 파일 끝에 다음 코드를 추가합니다.
## Global variable
my_variable = "I am the global version"
def another_function():
## This local variable shadows the global one
my_variable = "I am the local version"
print(f"\nInside another_function(): {my_variable}")
## 함수 호출
another_function()
## 전역 변수는 변경되지 않음
print(f"Outside another_function(): {my_variable}")
파일을 저장하고 스크립트를 다시 실행합니다.
python ~/project/variable_scope.py
전체 출력은 다음과 같습니다.
Inside the function:
Accessing global_variable: I am a global variable
Accessing local_variable: I am a local variable
Outside the function:
Accessing global_variable: I am a global variable
Inside another_function(): I am the local version
Outside another_function(): I am the global version
another_function() 내부에서 my_variable은 지역 버전을 참조합니다. 외부에서는 전역 버전을 참조합니다. 함수 내에서의 할당은 전역 변수에 영향을 미치지 않았습니다.
global 키워드를 사용하여 전역 변수 수정하기
기본적으로 함수 내부에서는 전역 변수의 값을 변경할 수 없습니다. 함수 내의 할당문은 새로운 지역 변수를 생성합니다. 전역 변수를 명시적으로 수정하려면 global 키워드를 사용해야 합니다.
WebIDE 에서 modify_global.py 파일을 엽니다.
전역 카운터와 이를 증가시키는 함수를 정의하는 다음 코드를 추가합니다.
## Global variable
counter = 0
def increment_counter():
## 전역 counter 를 수정할 의도가 있음을 선언
global counter
counter += 1
print(f"Inside function: counter is {counter}")
## 초기 값 출력
print(f"Before calling function: counter is {counter}")
## 전역 변수를 수정하기 위해 함수 호출
increment_counter()
## 함수 호출 후 값 출력
print(f"After calling function: counter is {counter}")
파일을 저장하고 터미널에서 실행합니다.
python ~/project/modify_global.py
출력은 전역 변수가 성공적으로 수정되었음을 보여줍니다.
Before calling function: counter is 0
Inside function: counter is 1
After calling function: counter is 1
global counter 문은 이 함수 내에서 counter에 대한 모든 연산이 새로운 지역 변수가 아닌 전역 변수에 영향을 미치도록 Python 에게 지시합니다.
이것과 대조하기 위해 global 키워드를 사용하지 않는 또 다른 함수를 추가해 보겠습니다. modify_global.py 끝에 다음 코드를 추가합니다.
def increment_counter_local():
## 이 할당은 'counter'라는 이름의 새로운 지역 변수를 생성합니다
counter = 100
print(f"\nInside function (local): counter is {counter}")
## 테스트 전 전역 counter 값 출력
print(f"Before calling function (local test): counter is {counter}")
## 함수 호출
increment_counter_local()
## 전역 counter 값은 영향을 받지 않음
print(f"After calling function (local test): counter is {counter}")
파일을 저장하고 스크립트를 다시 실행합니다.
python ~/project/modify_global.py
전체 출력은 차이점을 명확하게 보여줍니다.
Before calling function: counter is 0
Inside function: counter is 1
After calling function: counter is 1
Before calling function (local test): counter is 1
Inside function (local): counter is 100
After calling function (local test): counter is 1
두 번째 테스트에서 counter = 100 할당은 increment_counter_local() 내의 지역 변수에만 영향을 미쳤습니다. 전역 counter는 1이라는 값을 유지했습니다.
중첩 함수에서 nonlocal 변수 이해하기
Python 의 범위 규칙은 중첩 함수 (다른 함수 내부에 정의된 함수) 에도 적용됩니다. 내부 함수에는 지역적이지 않지만 외부 함수에는 지역적인 변수를 "비지역 변수 (nonlocal)"라고 합니다.
내부 함수에서 이러한 변수를 수정하려면 nonlocal 키워드를 사용해야 합니다. 이는 global이 작동하는 방식과 유사하지만, 전역 범위가 아닌 감싸는 범위 (enclosing scope) 에 적용됩니다.
WebIDE 에서 nonlocal_variable.py 파일을 엽니다.
nonlocal 사용법을 시연하기 위해 다음 코드를 추가합니다.
def outer_function():
outer_variable = "I am in the outer function"
def inner_function():
## 감싸는 범위 (enclosing scope) 의 변수를 수정하고 있음을 선언
nonlocal outer_variable
outer_variable = "I have been modified by the inner function"
print(f"Inside inner_function(): {outer_variable}")
print(f"Before calling inner_function(): {outer_variable}")
inner_function()
print(f"After calling inner_function(): {outer_variable}")
## 외부 함수 호출
outer_function()
파일을 저장하고 터미널에서 실행합니다.
python ~/project/nonlocal_variable.py
출력은 내부 함수가 외부 함수의 변수를 성공적으로 수정했음을 보여줍니다.
Before calling inner_function(): I am in the outer function
Inside inner_function(): I have been modified by the inner function
After calling inner_function(): I have been modified by the inner function
nonlocal outer_variable 문을 통해 inner_function은 outer_function의 outer_variable을 다시 바인딩 (rebind) 할 수 있습니다.
이제 nonlocal 키워드가 없을 때 어떤 일이 발생하는지 살펴보겠습니다. nonlocal_variable.py 끝에 다음 코드를 추가합니다.
def outer_function_local_test():
outer_variable = "I am in the outer function (local test)"
def inner_function_local_test():
## 이 할당은 새로운 지역 변수를 생성합니다
outer_variable = "I am a local variable in inner_function"
print(f"\nInside inner_function_local_test(): {outer_variable}")
print(f"\nBefore calling inner_function_local_test(): {outer_variable}")
inner_function_local_test()
print(f"After calling inner_function_local_test(): {outer_variable}")
## 지역 테스트를 위해 외부 함수 호출
outer_function_local_test()
파일을 저장하고 스크립트를 다시 실행합니다.
python ~/project/nonlocal_variable.py
전체 출력은 차이점을 강조합니다.
Before calling inner_function(): I am in the outer function
Inside inner_function(): I have been modified by the inner function
After calling inner_function(): I have been modified by the inner function
Before calling inner_function_local_test(): I am in the outer function (local test)
Inside inner_function_local_test(): I am a local variable in inner_function
After calling inner_function_local_test(): I am in the outer function (local test)
두 번째 예시에서 inner_function_local_test() 내부의 할당은 새로운 지역 변수를 생성했으며, 감싸는 범위의 outer_variable은 변경되지 않은 상태로 유지되었습니다.
요약
본 실습 (lab) 에서는 Python 함수와 관련된 몇 가지 주요 개념을 살펴보았습니다. 명시적인 return 문이 없는 함수는 암묵적으로 None을 반환한다는 것을 배우면서 시작했습니다. 그런 다음, 튜플 (tuple) 로 여러 값을 반환하는 것을 포함하여 함수에서 값을 되돌려 보내기 위해 return 키워드를 사용하는 연습을 했습니다.
또한 변수 범위 (scope) 를 깊이 탐구하여, 함수 내에서만 접근 가능한 지역 변수 (local variables) 와 스크립트 전체에서 접근 가능한 전역 변수 (global variables) 를 구별했습니다. 변수 섀도잉 (shadowing) 이 어떻게 작동하는지, 그리고 함수 내부에서 전역 변수를 수정하기 위해 global 키워드를 사용하는 방법을 배웠습니다. 마지막으로, 중첩 함수 (nested functions) 를 검토하고 nonlocal 키워드를 사용하여 감싸는 범위 (enclosing scope) 의 전역적이지 않은 변수를 수정하는 방법을 살펴보았습니다.



