함수

함수 에서 Python

{other: "%{count}개의 연습 문제"}

함수 소개

function은 특정 작업을 수행하기 위해 사용되는, 잘 짜인 재사용 가능한 코드 블록이에요. Functions는 애플리케이션을 더 잘 모듈화해 주고 코드를 높은 수준으로 재사용할 수 있게 해줘요. Python은 다른 프로그래밍 언어들처럼 바로 쓸 수 있는 내장 함수(print, map 등)를 제공해요. 직접 함수를 정의할 수도 있는데, 이런 함수를 user-defined functions라고 해요. 함수는 콘솔에 메시지를 출력하는 것처럼 간단한 일부터 꽤 복잡한 일까지 할 수 있어요.

함수 안의 코드를 실행하려면 함수를 호출해야 하는데, 함수 이름 뒤에 괄호 [()]를 붙이면 돼요. arguments라고 하는 데이터는 괄호 안에 넣어 함수에 전달할 수 있어요. 함수 정의에는 parameters가 하나도 없을 수도 있고 여러 개일 수도 있어요. 매개변수는 함수가 어떤 인자를 받아들일지 정의해요. 매개변수는 어떤 _타입_의 인자가 전달될지를 정해요. 인자는 함수가 호출될 때 실제로 전달되는 _값_이에요. 함수는 매개변수에 전달되는 인자에 따라 서로 다른 작업을 할 수 있어요.

함수는 return 키워드를 사용해 값을 반환할 수도 있어요. 함수가 반환하는 값을 return value라고 해요. 반환값은 함수를 호출한 쪽으로 돌아가요.

함수 만들기

Python에서는 def 키워드로 함수를 만들어요. def 뒤에는 함수 이름과 괄호 [()]가 와요. 괄호 안에는 매개변수를 쉼표로 구분해서 적어요. 닫는 괄호 뒤에 오는 콜론(:)은 함수 시그니처와 함수 본문을 구분해요.

함수 본문은 함수가 호출될 때 실행되는 코드 블록이에요. 함수 본문은 들여쓰기해요. 들여쓰기는 중요해요. Python은 들여쓰기를 보고 코드 블록이 어디서 끝나는지 알기 때문이에요. return 키워드를 사용하면 함수에서 값을 반환할 수 있고, 이 값은 함수를 호출한 쪽에서 사용할 수 있어요.

def function_name(parameter1, parameter2, ...):
    # function body
    return parameter1 + parameter2

매개변수나 반환값이 없는 함수를 정의할 수도 있어요.

def function_name():
    # function body
    pass

함수에는 본문이 반드시 있어야 해요. 본문이 아무 일도 하지 않더라도 말이에요. 그렇지 않으면 프로그램을 실행할 때 들여쓰기 오류가 발생해요:

>>> def my_bodyless_func():
...      

File ~/temp.py:1
    
    ^
IndentationError: expected an indented block

함수 호출하기

함수를 호출할 때는 함수 이름 뒤에 괄호 [()]를 붙여요. 함수에 전달하는 인자는 괄호 안에 쉼표로 구분해서 넣어요.

다음 함수를 살펴봐요:

def greet():
    print("Hello")

위 함수는 다음과 같은 문법으로 호출할 수 있어요:

>>> greet()
Hello

매개변수와 인자

인자는 함수가 호출될 때 함수에 전달되는 값이에요. 인자는 다른 함수나 클래스를 포함해 어떤 데이터 타입이든 될 수 있어요.

두 수를 더하는 add 함수를 정의해 봐요:

def add(first, second):
    print(first + second)

매개변수 first와 second는 add 함수가 어떤 인자를 받을지 정의해요. (parameter와 argument라는 단어는 종종 부정확하게 혼용되기도 해요.) 함수가 호출되면 인자가 함수로 전달돼요. 두 매개변수 모두에 인자를 전달해야 해요. 그렇지 않으면 TypeError가 발생해요.

>>> add(2, 3)
5

# Function can be called multiple times, with different parameters
>>> add(4, 3)
7
>>> add(5, 6)
11

# Passing an incorrect number of parameters will raise a `TypeError`
>>> add(2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: add() missing 1 required positional argument: 'second'

>>> add(2, 3, 4)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: add() takes 2 positional arguments but 3 were given

반환값

반환값은 함수를 호출한 쪽으로 돌아가는 값이에요. return value는 다른 함수나 클래스를 포함해 어떤 데이터 타입이든 될 수 있어요. 함수를 호출한 쪽은 이 값을 사용해 추가 작업을 할 수 있어요. 함수가 return value를 명시적으로 정의하지 않으면 Python 인터프리터가 None 값을 반환해요.

add 함수를 정의해 봐요:

def add(first, second):
    return first + second

반환값을 변수에 저장한 다음 출력할 수 있어요:

>>> result = add(2, 3)
>>> print(result)
5
# Type of result is `int`
>>> type(result)
<class 'int'>

# We can also perform operations on the return value
>>> result * 2
10

# A function without an explicit return value will return `None`
>>> def log(message):
    print(message)

# Hello is printed because of print(message), but return value is `None`
>>> return_value = log("Hello")
Hello
>>> return_value
None

return을 사용하면 함수가 즉시 종료되고 값이 호출한 쪽으로 반환돼요.

>>> def show(first, second):
    print(first)
    return first
    print(second)

# second never gets printed, because the function exits after the return statement
>>> show(1, 2)
1

모듈성

복잡한 프로그램은 더 작은 부분으로 나눌 수 있어요. 서로 다른 함수로 각각의 구체적인 작업을 수행할 수 있어요.

프로그램이 다음과 같은 작업을 해야 한다고 가정해 봐요:

  • 원의 넓이 구하기
  • 직사각형의 넓이 구하기
  • 삼각형의 넓이 구하기

프로그램을 더 작은 부분으로 나눌 수 있어요.

def circle_area(radius):
    return 3.14 * radius * radius

def rectangle_area(length, breadth):
    return length * breadth

def triangle_area(base, height):
    return 0.5 * base * height

이제 원하는 순서대로 함수를 호출할 수 있어요.

>>> circle_area(2)
12.56
>>> triangle_area(2, 3)
3.0
>>> rectangle_area(2, 3)
6
>>> rectangle_area(1, 2) + circle_area(2) + triangle_area(1, 2)
15.56

변수의 스코프

변수(또는 이름)가 함수 안에서 정의되면, 함수 스코프(또는 지역 네임스페이스) 안에서만 접근할 수 있어요. 함수 스코프 밖에 같은 이름의 변수가 있더라도 마찬가지예요.

함수 밖, _모듈 수준_에서 정의된 변수는 _전역 네임스페이스_에 있는 것으로 봐요. 함수 밖이지만 둘러싸는 함수나 클래스 안에서 정의된 변수는 _비지역 네임스페이스_에 있어요.

Python은 프로그램이 실행될 때 변수 이름을 찾기 위해 LEGB 규칙(Local, Enclosing, Global, Built-in)을 사용해요:

  1. 지역(또는 함수) 네임스페이스에서 이름을 찾아요.
  2. 지역에서 이름을 찾지 못하면 둘러싸는(또는 비지역) 네임스페이스에서 찾아요.
  3. 비지역/둘러싸는 네임스페이스에서 찾지 못하면 전역 네임스페이스(모듈 수준)에서 찾아요.
  4. 전역에서 찾지 못하면 내장(프로그램 또는 Python 전체) 네임스페이스에서 찾아요.

그래도 이름을 찾지 못하면 Python은 NameError exception을 발생시켜요.

# Global namespace.
general_favorite = "apples"


def alices_favorite():
   # Local namespace.  
   favorite = "cherries"
   
   # This works because Python will eventually find general_favorite in the global namespace.
   # Python will find 'print' in the built-in namespace.
   print(f'Alice has always liked {favorite}, but most people like {general_favorite}.')

   
def our_favorite_fruits():
    # Enclosing or nonlocal namespace.
    yours = "peaches"
    
    def my_favorite():
        # Local namespace.
        mine = "pears"
        
        # This works because Python will eventually find 'yours' in the enclosing/nonlocal namespace.
        print(f'My favorite is {mine}, but you like {yours} instead.')
        
        # This works because Python will eventually find 'general_favorite' in the global namespace.
        print(f'Everyone seems to like {general_favorite}')
    
    # This function is in the local namespace of the 'our_favorite_fruits' function.  
    my_favorite()

    
# This will raise NameError: name 'favorite' is not defined, because the variable favorite is local to alices_favorite.
print(favorite)

변수 이름을 지역 함수 스코프 밖에서 접근할 수 있게 하려면(또는 함수 스코프 밖에 정의된 변수를 수정하려면), global이나 nonlocal 키워드를 사용해야 해요.

global 키워드를 사용하면 Python은 _전역 네임스페이스_에서 이름을 찾기 시작해요. 그러면 변수에 값을 대입할 때 지역 버전을 새로 만드는 대신 전역 변수를 수정해요. global로 변수를 선언하면 그 변수는 전역 네임스페이스에 _추가_돼요. 따라서 global은 신중하게 사용해야 해요. 전역 변수를 추가하거나 수정하면 그 값을 사용하는 다른 모든 코드에 영향을 줄 수 있기 때문이에요.

nonlocal 키워드는 Python에게 _비지역 또는 둘러싸는 네임스페이스_에서 변수를 찾거나 만들라고 알려줘요. 함수가 다른 함수나 클래스 안에 중첩되어 있고, 바깥 함수의 변수와 스코프에 접근해야 할 때 사용해요.

일급 객체로서의 함수

Python에서는 함수를 변수에 대입하거나 다른 함수의 인자로 전달할 수 있어요. 반환값으로 사용할 수도 있어요. 함수는 시퀀스(list, tuple 등)에 넣거나 dict의 값으로 넣을 수도 있어요. 함수는 다른 객체를 쓸 수 있는 곳이면 어디든 사용할 수 있어요. 함수는 일급 객체이기 때문이에요. 그래서 함수는 특별한 속성을 가지고 있고, 직접 사용자 정의 속성을 가져오고 설정하는 것까지 정의할 수 있어요.

# print is a function

# A function can be assigned to a variable.
>>> function_as_variable = print
>>> function_as_variable("Hello")
Hello
>>> type(fake_print)
<class 'builtin_function_or_method'>

# Functions can be passed as an argument to another function.
>>> def check_print(func):
    func("Hello")
>>> check_print(print)
Hello

# Function can be used as an item in a sequence.
>>> my_list = [print, "Hello"]
>>> my_list[0]("Hello")
Hello

# Functions can be used as a value in a dictionary.
>>> my_dict = {"key": print}
>>> my_dict["key"]("Hello")
Hello

# Functions can be returned from a function.
>>> def return_func():
    return print
>>> return_func()("Hello")
Hello

함수는 다른 함수 안에 중첩할 수도 있어요.

def outer():
    num = 10
    
    # This is a nested, or "inner" function.
    def inner():
        print(num)
    
    # Outer function calling inner function
    inner()

내부 함수는 바깥 함수에서 정의한 num 변수에 접근할 수 있어요.

>>> outer()
10

특별한 속성

Python의 함수에는 특별한 속성이 있어요. 그중 몇 가지는 다음과 같아요:

  • __name__: 함수의 이름
  • __doc__: 함수의 문서 문자열
  • __module__: 함수가 정의된 모듈
  • __globals__: 함수의 전역 변수 딕셔너리
  • __code__: 함수의 명령을 담고 있는 코드 객체
>>> def add(first, second):
      return first + second

# Function name
>>> print.__name__
'print'
>>> len.__name__
'len'
>>> add.__name__
'add'

# Function documentation
>>> abs.__doc__
'Return the absolute value of the argument.'

# Module
>>> len.__module__
'builtins'
>>> add.__module__
'__main__'

함수 속성의 전체 목록은 Python 데이터 모델에서 볼 수 있어요.

GitHub에서 편집 링크가 새 창이나 탭에서 열려요