스터디/파이썬 스터디 강의자료

[3팀/이지현] 5차시 파이썬 스터디 - 함수

알 수 없는 사용자 2023. 4. 2. 23:44

 

5차시_함수_강의안.pdf
0.59MB
5차시_함수_과제.pdf
0.21MB

본 강의 자료는 데이터 과학을 위한 파이썬 프로그래밍/최성철을 참고

하여 제작하였습니다.

🖋️  함수 기초

함수의 개념과 장점

  1. 개념

함수는 어떤 일을 수행하는 코드의 덩어리, 또는 코드의 묶음이라고 표현할 수 있습니다.

  1. 예시

만약 도형의 넓이를 구하는 프로그램에서 사각형의 넓이를 구하는 작업이 있다면, 해당 작업을 함수화하여 필요할 때마다 호출하는 것입니다.

  1. 장점
  • 필요할 때마다 호출 가능 : 함수는 반복적으로 수행해야 하는 업무를 한 번만 작성해 놓고 필요할 때마다 호출하여 사용할 수 있습니다. 이렇게 하면 코드를 수정할 때 같은 작업을 여러 번 반복하지 않아도 되겠죠?
  • 논리적인 단위로 분할 가능 : 함수는 코드를 논리적인 단위로 나눌 수 있습니다. 간단히 도형을 계산하는 프로그램을 작성하더라도 곱셈을 하는 코드, 덧셈을 하는 코드, 나눗셈을 하는 코드 등으로 나눌 수 있습니다.
  • 코드의 캡슐화 : 함수는 코드의 캡슐화가 가능합니다. 캡슐화는 함수의 인터페이스만 잘 정의하면 다른 사람이 자신의 코드를 쉽게 가져다 사용할 수 있는 특징이 있습니다. 인터페이스를 정의한다는 것은 코드에 입력되는 입력값과 코드의 수행 결과인 출력값을 명확히 한다는 것인데, 이 인터페이스가 잘 정의된 함수라면 코드의 내부 구조를 몰라도 함수를 사용하는 데에는 아무 문제가 없습니다.

함수의 선언

  1. 방법
def 함수 이름 (매개변수 #1 ...): 
    명령문 1
    명령문 2
    return <반환값>
  • def : ‘definition’의 줄임말로, 함수의 정의를 시작한다는 의미입니다.
  • 함수 이름 : 함수 이름은 개발자가 마음대로 지정할 수 있지만 일반적으로 파이썬에서는 다음과 같은 규칙을 사용합니다.
  • 1. 소문자로 입력한다. 2. 띄어쓰기를 할 경우에는 _ 기호를 사용한다. ex) save_model 3. 작업을 나타내기 위해 동사와 명사를 함께 사용하는 경우가 많다. ex) find_number 4. 외부에 공개하는 함수일 경우 줄임말을 사용하지 않고 짧고 명료한 이름으로 정한다.
  • 매개변수(parameter) : 매개변수는 함수에서 입력값으로 사용하는 변수를 의미하며, 1개 이상의 값을 적을 수 있습니다.
  • 명령문 : 명령문은 반드시 들여쓰기를 한 후에 코드를 입력해야 합니다. 수행해야 하는 코드는 지금까지 배운 일반적인 코드 즉, if나 for문 등의 제어문을 사용할 수도 있고, 고급 프로그래밍 기법으로 함수 안에 함수를 사용하기도 합니다.
  1. 예시
def calculate_rectangle_area(x, y)
    return x * y

함수 이름은 calculate_rectangle_area이고, x와 y라는 2개의 매개변수를 사용하고 있습니다. 여기서 return의 의미는 값을 반환한다는 뜻으로, x와 y를 곱한 값을 반환하는 함수로 이해하면 됩니다.

  1. 반환?

약간 어렵게 느껴질 수 있는 부분이 바로 '반환’이라는 개념인데요. 이는 수학에서 함수와 같은 개념이라고 생각하시면 됩니다.

예를 들어서 수학에서 f(x) = x + 1 이라고 했을 때, f(1)의 값은 얼마일까요? 저희는 2라는 것을 알 수 있습니다. 즉, 함수 f(x)에서 x에 1이 들어가면 2가 반환되는 것입니다.

파이썬의 함수도 같은 개념입니다! 수학에서 x에 해당하는 것이 매개변수, 즉 입력값이고 x + 1의 계산 과정이 함수 안의 코드이며 그 결과가 반환되는 값입니다.

예시와 함께 함수 실행 순서 알아보기

  1. 예시 코드
def calculate_rectangle_area(x, y):
    return x * y

rectangle_x = 10
rectangle_y = 20
print("사각형 x의 길이:", rectangle_x)
print("사각형 y의 길이:", rectangle_y)

print("사각형의 넓이:", calculate_rectangle_area(rectangle_x, rectangle_y))
사각형 x의 길이: 10 
사각형 y의 길이: 20 
사각형의 넓이: 200
  1. 순서 분석

첫 번째 : 1행에서 함수가 정의된 def 부분은 실행되지 않습니다. 단지 정의만 하는 것으로 함수만 파일에 저장하고 파이썬 명령으로 실행시켰을 때 별도의 출력이 일어나지 않습니다. 정확히 말하면, 아무것도 실행되지 않는 것처럼 보이지만 실제로는 해당 코드를 메모리에 업로드하여 다른 코드를 호출해 사용할 수 있도록 준비 과정을 거치는 것입니다.

두 번째 : 함수가 수행해야 하는 명령이 실행됩니다. 정확히는 rectangle_x = 10과 rectangle_y = 20으로 2개의 변수에 값이 할당되고 그 값을 출력합니다. (6, 7행의 print문 실행)

세 번째 : print("사각형의 넓이:", calculate_rectangle_area(rectangle_x, rectangle_y))를 수행합니다. 이 부분에서 위에서 정의했던 함수를 호출하고, rectangle_x와 rectangle_y 변수에 할당된 값이 calculate_rectangle_ area 함수의 입력값이 됩니다. 그러면 코드 return x * y에 의해 반환값 200이 반환되어 200이 출력됩니다.

매개변수와 인수 구분하기

함수의 입력값은 매개변수로 구분되어 사용합니다. 함수와 관련해 몇 가지 용어에 대한 이해가 필요한데요, 예시를 통해 대표적인 매개변수(parameter)와 인수(argument)의 차이를 이해해봅시다.

def f(x):
    return 2 * x + 7

print(f(2))
11

매개변수는 함수의 인터페이스 정의에 있어 어떤 변수를 사용하는지 정의하는 것입나다. 즉, 위 함수에서는 x가 해당 함수의 매개변수입니다.

그에 반해 인수는 실제 매개변수에 대입되는 값을 뜻합니다. 위 코드에서는 f(2)에서 2가 인수에 해당합니다.

함수의 형태

  1. 구분

함수는 매개변수와 반환값의 유무에 따라 네 가지 형태로 구분합니다.

반환값 & 매개변수 유무 매개변수 없음 매개변수 있음

반환값 없음 함수 내부 명령문만 수행 매개변수를 사용하여 명령문만 수행
반환값 있음 매개변수 없이 명령문을 수행한 후 결과값 반환 매개변수를 사용하여 명령문을 수행한 후 결과값 반환
  1. 예시를 통해 비교하기
def a_rectangle_area():       # 매개변수 x, 반환값 x
    print(5 * 7)
def b_rectangle_area(x, y):   # 매개변수 o, 반환값 x
    print(x * y)
def c_rectangle_area():       # 매개변수 x, 반환값 o
    return(5 * 7)
def d_rectangle_area(x, y):   # 매개변수 o, 반환값 o
    return(x * y)

a_rectangle_area()
b_rectangle_area(5, 7)
print(c_rectangle_area())
print(d_rectangle_area(5, 7))
35
35
35
35
  • 첫 번째 함수 (매개변수 x, 반환값 x) : 이는 매개변수와 반환값이 모두 없는 경우입니다. 입력값도 없고 반환되는 값도 없지만, print(5 * 7)로 인해 35가 출력됩니다. 이 경우에는 a_rectangle_area()가 35로 치환되는 것이 아니고, 반환값이 없기 때문에 함수 자체는 none값을 가지게 됩니다. 대신 내부 함수인 print() 함수로 인해 35가 출력되는 것입니다.
  • 두 번째 함수 (매개변수 o, 반환값 x) : 이는 b_rectangle_area()가 매개변수로 x, y를 넘겨받고, 그 값을 계산하여 화면에 출력하는 함수입니다. 이 역시 반환값이 없으므로 11행에서 b_rectangle_area(5, 7)이라고 실행시켜야 35가 출력되는 것이지, b_rectangle_area(5, 7) 자체가 35로 치환되는 것은 아닙니다. 반환이 없으면 해당 함수는 none으로 치환되는 것입니다.
  • 세 번째, 네 번째 함수 (매개변수 x, 반환값 o / 매개변수 o, 반환값 o) : 이들은 모두 반환값이 있는 경우입니다. c_rectangle_area()와 d_rectangle_ area() 함수 모두, 함수 안에서 print() 함수를 수행시키는 것이 아니라 함수를 호출할 때 print() 함수를 이용한다는 것을 알 수 있습니다. 즉, 두 함수 모두 return문으로 인해 35로 치환되는 것입나다. 이렇게 return이 있는 경우, 즉 함수의 반환값이 있는 경우에는 그 함수를 호출 하는 곳에서 함수의 반환값을 변수에 할당하여 사용하는 것이 가능합니다.
  • 이렇게 모두 35라는 같은 값을 출력하지만,함수의 형태에 따라 사용하는 방법이 다르다는 것을 알 수 있습니다.

🖋️ 함수 심화

함수의 호출 방식

  1. 전통적인 프로그래밍에서 함수가 변수를 호출하는 방식

종류 설명

값에 의한 호출  
(call by value) • 함수에 인수를 넘길 때 값만 넘김
• 함수 내부의 인수값 변경 시 호출된 변수에 영향을 주지 않음  
참조 호출  
(call by reference) • 함수에 인수를 넘길 때 메모리 주소를 넘김
• 함수 내부의 인수값 변경 시 호출된 변수값도 변경됨  

하지만, 파이썬은 전통적인 두 가지 방식을 혼합한 조금은 특이한 방식을 사용합니다.

  1. 파이썬에서 함수가 변수를 호출하는 방식

파이썬은 객체의 주소가 함수로 넘어간다는 뜻의 객체 호출(call by object reference) 방식을 사용합니다. 파이썬에서는 객체의 주소를 함수로 넘기기 때문에 전달된 객체의 값에 변경이 있을 경우, 즉 새로운 값을 할당하거나 해당 객체를 지울 때는 영향을 주지 않지만 단순히 해당 객체에 값을 추가할 때는 영향을 주게 됩니다.

  1. 예시를 통해 이해하기
def spam(eggs):
    eggs.append(1)   # 기존 객체의 주소값에 [1] 추가
    eggs = [2, 3]    # 새로운 객체 생성

ham = [0]
spam(ham)
print(ham)
[0, 1]

위 코드의 ham과 eggs, 두 리스트에서 변수를 사용한 것을 보면 좀 더 명확히 알 수 있습니다. 5행부터 보시면 ham이라는 리스트를 만들고, 함수 spam에 ham을 인수로 입력히였습니다.

이때, 함수 안에서는 이름이 ham에서 eggs로 바뀌게 됩니다. ham과 eggs는 함수의 호출 방식으로 객체를 호출하고 있으므로 같은 주소를 공유합니다. 따라서 2행의 eggs. append(1)에 의해 해당 리스트에 1이 추가되면 ham과 eggs가 모두 영향을 받습니다. 3행에서는 eggs = [2, 3]으로 새로운 리스트를 만들었습니다. 그래서 더 이상 ham과 eggs가 같은 메모리 주소를 갖지 않고 eggs는 자기만의 메모리 주소를 가지게 됩니다. 그리고 함수를 빠져나와 7행의 print(ham)이 실행되면, 2행의 eggs.append(1)에 의해 추가된 [0, 1]이 화면에 출력됩니다.

이것이 바로 **객체 호출(call by object reference)**이라는 파이썬의 함수 내부 변수 호출 방식입니다.

3차시 조건문 과제 답안

변수의 사용 범위

  1. 정의

변수의 사용 범위는 말 그대로 변수가 코드에서 사용되는 범위를 말하는데, 흔히 함수 내부에서 사용할 것인지, 또는 프로그램 전체에서 사용할 것인지 그 범위를 결정하는 규칙을 뜻합니다.

  1. 종류

일반적으로 변수의 사용 범위를 결정할 때 고려해야 할 두 가지 변수가 있습니다.

지역 변수(local variable): 함수 내부에서만 사용

전역 변수(global variable): 프로그램 전체에서 사용

  1. 사용 예시 1
def test(t):
    print(x)
    t = 20
    print("In Function:", t)

x = 10
test(x)
print("In Main:", x)
print("In Main:", t)
10
In function: 20
In Main: 10
Traceback (most recent call last):
    File "scoping_rule.py", line 9, in <module>
        print("In Main:", t)
NameError: name 't' is not defined

여기서 중요한 것은 변수 x와 t입니다. 프로그램이 가장 먼저 시작되는 지점은 6행의 x = 10 입니다. 그리고 7행에서 x는 test(x) 함수의 인수로 넘어가게 됩니다. 그렇다면 함수 내부에서 처음 만나는 2행 print(x)의 x는 어떤 변수일까요?

이때의 x는 함수 내부에서 재정의되지 않았으므로 함수를 호출한 메인 프로그램에서 정의한 x = 10의 x를 뜻합니다. 즉, 프로그램 전체에서 사용할 수 있는 전역 변수입니다.

함수 내부에 있는 t는 어떨까요? test(x) 함수의 x를 t로 치환하여 사용합니다. 즉, 함수 내부에서는 x를 따로 선언한 적은 없고, t를 선언하여 사용합니다. 그리고 3행의 t = 20에 의해 t에 20이 할당되고, 4행 print(”In Function:", t)의 결과로 In Function: 20이 출력됩니다.

하지만 함수가 종료되고 코드에 9행의 print(”In Main:", t)가 실행되면 오류가 출력되는데요. 왜냐하면 t가 함수 내부에서만 사용할 수 있는 지역 변수이기 때문입니다. 그래서 메인 함수에서 t 변수를 호출하는 것은 불가능합니다.

  1. 사용 예시 2
def f():
    s = "I love London!"
    print(s)

s = "I love Paris!"
f()
print(s)
I love London!
I love Paris!

이 예시에서 변수 s는 f( ) 함수의 내부에서도 사용되고 외부에서도 사용됩니다. s의 값은 어떻게 바뀔까요?

프로그램이 시작되자마자 s에는 ‘I love Paris!’ 값이 할당됩니다. 하지만 그 후 함수 내부로 코드의 실행이 옮겨가 다시 s에 ‘I love London!’ 값이 저장되고, 그 값이 먼저 출력됩니다.

그렇다면 함수 외부에 있는 변수 s의 값은 변경이 되었을까요? 함수가 종료된 후 7행 print(s)의 실행 결과는 ‘I love Paris!’입니다. 바뀌지 않았는데요.

이는 함수 내부와 외부에 있는 변수 s는 같은 이름을 가졌지만 사실 다른 메모리 주소를 갖고 있는 전혀 다른 변수이기 때문입니다. 따라서 함수 내부의 s는 해당 함수가 실행되는 동안에만 메모리에 있다가 함수가 종료되는 순간 사라지는 것입니다. 당연히 함수 외부의 s와는 메모리 주소가 달라 서로 영향을 주지 않는다. 즉, 변수의 이름이 같다고 다 같은 함수가 아니라는 뜻이죠?

  1. 사용 예시 3 (global 사용하기)

함수 내부의 변수와 함수 외부의 변수가 같은 메모리 주소를 갖는 같은 변수를 사용하기 위해서는 어떻게 해야 할까요?

전역 변수로 선언하면 됩니다. 함수 내에서 변수를 전역 변수로 선언하기 위해서는 파이썬에서 제공하는 global 키워드를 사용합니다.

def f():
    global s
    s ="I love London!"
    print(s)

s = "I love Paris!"
f()
print(s)
I love London!
I love London!

위의 사용 예시 2 코드에서 변경된 것은 함수 내부에서 2행의 global s 코드 입니다. 이 코드의 결과는 이전과 다르게 출력되는데요. 그 이유를 살펴봅시다.

기존의 s에는 ‘I love Paris!’가 저장되어 있는데. f( ) 함수 내부의 코드가 수행되는 순간 global s가 선언되어 함수 외부의 s, 즉 전역 변수 s의 메모리 주소를 사용하게 됩니다. 그래서 이전과 달리 함수 내부와 외부에 있는 s는 같은 메모리 주소를 사용하게 됩니다. 따라서 해당 메모리 주소에 새로운 값인 ‘I love London!’이 할당되면 함수 외부의 s도 해당 값이 할당되어 위와 같은 결과가 출력되는 것입니다.

재귀 함수

  1. 정의

재귀 함수(recursive function)는 자기 자신을 다시 호출하는 함수입니다. 재귀적이라는 표현은 자신을 이용해 다른 것을 정의한다는 뜻이죠. 재귀 함수는 수학의 점화식과 같은 형태입니다.

  1. 예시

위 수식이 팩토리얼 함수입니다. 정확히는 'n!'으로 표시하고 n! = n x (n - 1)로 선언할 수 있습니다. 자신의 숫자에서 1씩 빼면서 곱하는 형식이며 보통은 점화식이라고 합니다.

이러한 형태의 수식을 코드로 표현하면 다음과 같습니다. (사용자가 5 입력)

def factorial(n):
    if n == 1:
        return 1 
    else:
        return n * factorial(n - 1)

print(factorial(int(input("Input Number for Factorial Calculation: "))))
Input Number for Factorial Calculation: 5
120

위 코드에서 factorial() 함수는 n이라는 변수를 입력 매개변수로 넘겨받은 후 n == 1이 아닐 때까지 입력된 n과 n에서 1을 뺀 값을 입력값으로 다시 factorial() 함수를 호출하여 반환된 값과 곱합니다.

만약 처음 사용자가 5를 입력했다면 다음과 같은 순서대로 계산될 것입니다.

5 * factorial(5 - 1) = 5 * 4 * factorial(4 - 1) = 5 * 4 * 3 * factorial(3 - 1) = 5 * 4 * 3 * 2 * factorial(2 - 1) = 5 * 4 * 3 * 2 * 1

🖋️ 함수의 인수

파이썬에서 인수를 사용하는 방법

파이썬에서는 다양한 방식으로 인수의 사용 방법을 지원합니다.

종류 내용

키워드 인수 함수의 인터페이스에서 지정한 변수명을 사용하여 함수의 인수를 지정하는 방법
디폴트 인수 별도의 인수값이 입력되지 않을 때 인터페이스 선언에서 지정한 초깃값을 사용하는 방법
가변 인수 함수의 인터페이스에서 지정한 변수 이외의 추가 변수를 함수에 입력할 수 있도록 지원하는 방법
키워드 가변 인수 매개변수의 이름을 따로 지정하지 않고 입력하는 방법

키워드 인수

  1. 내용

키워드 인수는 함수에 입력되는 매개변수의 변수명을 사용하여 함수의 인수를 지정하는 방법입니다.

  1. 예시
def print_something(my name, your name):
    print("Hello {0}, My name is {1}".format(your _name, my_name))

print_something("Sungchul", "TEAMLAB")
print_something(your_name = "TEAMLAB", my_name = "Sungchul")
Hello TEAMLAB, My name is Sungchul
Hello TEAMLAB, My name is Sungchul

위 코드에서 print_ something() 함수는 (my_name, your_name)이라는 입력 인터페이스를 가지게 됩니다. 일반적으로 함수를 호출할 때 인수가 순서대로 입력되도록 코드를 작성해야 하는데요. 따라서 4행의 print_something("Sungchul", "TEAMLAB")에서 ‘Sungchul’은 my_ name 에, 'TEAMLAB'은 your_name에 할당됩니다.

하지만 함수의 입력 변수명만 정확히 기재된다면, 순서에 상관없이 원하는 변수에 인수를 넣을 수도 있습니다. 5행의 print_something(your_ name = “TEAMLAB”, my_name = “Sungchul")을 보면 함수의 인수에서 사용되는 각각의 변수명을 명시함으로써 해당 변수에 값이 할당될 수 있도록 처리하였습니다. 즉 입력되는 순서에 상관없이 ‘Sungchul'은 my name으로, 'TEAMLAB'은 your name으로 할당되는 것입니다.

따라서 두 함수에 대한 2가지 호출 코드의 실행 결과가 동일하게 출력됩니다.

디폴트 인수

  1. 내용

디폴트 인수는 매개변수에 기본값을 지정하여 사용하고, 아무런 값도 인수로 넘어가지 않을 때 지정된 기본값을 사용하는 방법입니다.

  1. 예시
def print_something_2(my name, your name = "TEAMLAB"):
    print("Hello {0}, My name is {1}".format (your_name, my_name))

print_something_2("Sungchul", "TEAMLAB")
print_something_2("Sungchul")
Hello TEAMLAB, My name is Sungchul
Hello TEAMLAB, My name is Sungchul

1행을 보면 def print_something_2(my_name, your_name = "TEAMLAB")에서 함수의 인터페이스를 정의하였는데. your_name 매개변수에는 기본값으로 ‘TEAMLAB’이 지정된 것을 확 인할 수 있습니다. 따라서 이 경우에는, 함수를 호출할 때 your_name 매개변수에 별도의 값을 할당하지 않아도 함수가 작동합니다.

5행의 print_something_2("Sungchul")에서 함수의 매개변수가 2개인데도 인수를 하나만 입력하였습니다. 이 경우 입력된 값은 첫 번째 매개변수인 my_name에 할당되고, 두 번째 매개변수인 your_name에는 디폴트 인수로 지정된 ‘TEAMLAB’이 할당되는 것입니다. 결국 4행과 5행의 코드를 모두 실행하면 두 코드의 결과는 같습니다.

가변 인수

  1. 내용

코드를 작성할 때, 가끔 함수의 매개변수 개수를 정하지 않아야 하는 경우가 있습니다. 예를 들어, 항의 개수가 정해지지 않은 다항 방정식의 덧셈 연산이라든가, 고객이 물건을 얼마나 구매할지 모르는 마트 계산기에서 합산을 수행하는 연산 등이 그 예겠죠? 이런 경우에 사용하는 것이 바로 가변 인수입니다.

가변 인수는 *(asterisk라고 부름)로 표현할 수 있는데, *은 파이썬에서 기본적으로 곱셈 또는 제곱 연산 외에도 변수를 묶어주는 가변 인수를 만들 때 사용합니다. 가변 인수는 튜플 자료형으로 사용됩니다.

  1. 예시
def asterisk_test(a, b, *args):
    return a + b + sum(args)

print(asterisk_test(l, 2, 3, 4, 5))
15

위 코드의 asterisk_test() 함수는 변수 a, b를 넘겨받고, 나머지를 *args로 넘겨받고 있습니다. 여기서 args를 가변 인수라고 합니다.

4행의 asterisk_test(1, 2, 3, 4, 5)를 보면 1과 2는 각각 a와 b에 할당되고, 나머지 인수인 3, 4, 5가 모두 *args에 할당되어 15가 출력되는 것입니다.

키워드 가변 인수

  1. 내용

가변 인수는 변수의 순서대로 튜플 형태로 저장됩니다. 이를 사용할 때는 매우 간단하지만 변수의 이름을 지정할 수 없다는 단점이 있습니다. 이러한 단점을 보완하는 방법이 바로 키워드 가변 인수입니다.

이 방법은 매개변수의 이름을 따로 지정하지 않고 입력하는 방법으로 이전 가변 인수와는 달리 *을 2개 사용하여 함수의 매개변수를 표시합니다. 그리고 입력된 값은 튜플 자료형이 아닌 딕셔너리 자료형으로 사용합니다.

  1. 예시
def kwargs_test(**kwargs):
    print(kwargs)
    print("First value is {first}".format(**kwargs))
    print("Second value is {second}".format(**kwargs))
    print("Third value is {third}".format(**kwargs))

kwargs_test(first =3, second =4, third = 5)
{'first': 3, 'second': 4, 'third': 5}
First value is 3
Second value is 4
Third value is 5

키워드 가변 인수는 변수명으로 kwargs를 사용합니다. 사실 변수명 자체는 중요하지 않지만, * 는 반드시 **처럼 2개를 붙여야 한다는 것을 기억해주세요!

7행에서 3개의 키워드 인수를 넘겼으므로 2행의 print() 함수에서 그 인수들이 {'first': 3, 'second': 4, 'third': 5}형태, 즉 변수명과 값이 쌍으로 지정된 딕셔너리 형태로 출력되는 것을 확인할 수 있습니다.

키워드 가변 인수를 사용하여 ‘first = 3, second = 4, third = 5’와 같이 변수를 함수 안에 넣은 것입니다.