1. 함수
■ 입력값이 들어오면, 어떤 처리 단계를 거쳐 그 결과를 반환하는 것이 바로 함수가 하는 일이다.
■ 예를 들어, \( y = 2x + 3 \)는 입력 \( x \)에 따라 출력 \( y \) 값이 변하는 함수이다. 파이썬의 print(), abs() 와 같은 함수들도 입력값을 받아 결과를 출력하는 함수의 대표적인 예이다.
- '-10'을 print()의 입력으로 넣으면 -10을 출력한다. -10을 abs()의 입력으로 넣으면 절댓값 10을 반환한다.
1.1 함수의 필요성
■ 함수를 사용하면 동일한 내용을 반복하는 처리 단계를 하나로 묶어, 필요할 때 호출하여 사용할 수 있다. 특정 기능을 수행하는 코드의 묶음이라고 볼 수 있다. 이는 코드를 재활용하는 것으로 볼 수 있다.
■ 예를 들어, 어떤 색을 알려주는 프로그램이 있다고 했을 때, 아래의 첫 번째 코드는 파란색, 두 번째 코드는 빨간색임을 출력한다. 색의 이름만 다르고 다른 메시지는 동일하다.
print('Hello')
print('This color is blue.')
```#결과#```
Hello
This color is blue.
````````````
print('Hello')
print('This color is red.')
```#결과#```
Hello
This color is red.
````````````
■ 이러한 경우에 유용하게 사용할 수 있는 도구가 함수이다. 예시의 처리 단계를 하나로 모아서 필요할 때 언제든지 호출하여 사용할 수 있다.
def msg(color):
print('Hello')
print(f'This color is {color}.')
msg('blue')
```#결과#```
Hello
This color is blue.
````````````
msg('red')
```#결과#```
Hello
This color is red.
````````````
- 위와 같이 함수를 사용하는 것을 함수를 호출(call)한다고 한다.
1.2 사용자 정의 함수의 구조
■ 파이썬의 내장 함수 외에 값을 반환하는 자체 함수를 다음과 같이 정의할 수 있다.
def 함수이름(매개변수1, 매개변수2, ...):
명령문1
명령문2
...
return 반환값
■ 함수는 크게 헤더(header)와 몸체(body)로 나누어진다. 헤더는 def 키워드로 시작하는 부분이다.
1.2.1 함수 헤더(header)
■ 'def'는 함수를 만들 때 사용하는 예약어(키워드)이다. 함수 이름은 사용자가 임의로 만들 수 있다.
■ 함수이름과 함께 매개변수(파라미터)를 적어준다. 함수 이름 뒤 괄호 안의 매개변수는 함수에 입력으로 외부에서 전달되는 값을 받는 변수이다.
■ 매개변수를 적어준 다음, 콜론(:)을 찍어준다. 함수 헤더는 콜론으로 끝나야 한다.
1.2.2 함수 몸체(body)
■ 함수 몸체에는 함수가 수행해야 하는 명령문들이 똑같은 들여쓰기 간격으로 들어간다.
■ 함수 몸체에 return 키워드를 이용하여 명령문들이 계산한 결과를 반환할 수 있다. 함수가 유용한 것은 함수로부터 반환 값을 받을 수 있기 때문이다.
■ return은 함수 블록 내 어느 곳에나 나타날 수 있으며, 첫 번째 return 명령이 실행되는 즉시 종료한다.
■ 첫 번째 retrun 명령이 실행되는 즉시 종료되는 것을 함수를 빠져나가는 방법으로 이용할 수 있다.
def greet(word):
if word == 'abc':
return
print('ABC')
■ 위와 같이 if 조건문이 word == 'abc'이면 return이 실행되어 greet() 함수가 종료된다.
■ 값을 반환할 때 주의할 점은 하나의 경우에서만 값을 반환하면 안 된다는 점이다. 모든 경우에서 값을 반환해야 한다. 예를 들어, 다음과 같이 함수 내에서 if 문을 사용할 경우
def greet(word):
if word == 'abc':
return True
■ word가 'abc'가 아닌 경우, 그때의 값을 반환하는 문장이 없다. 그러므로 다음과 같이 해당 경우에 대해서도 return할 값을 지정해야 한다.
def greet(word):
if word == 'abc':
return True
else:
return None
■ 혹은 다음과 같이 반환할 값을 하나의 변수에 저장했다가 마지막에 return 문장을 사용하는 방법도 있다.
def greet(word):
if word == 'abc':
flag = True
else:
flag = False
return flag
1.3 함수 호출과 매개변수와 인수
■ 함수를 정의하는 이유는 함수를 사용하기 위해서이며, 함수를 사용하기 위해서는 함수를 호출(call)하면 된다.
■ 함수 호출이란 함수의 이름을 써주는 것을 말한다. 함수 안의 명령문들은 호출되기 전까지는 실행되지 않는다.
■ 함수가 호출되면, 함수 안에 있는 명령문들이 실행되며, 실행이 끝나면 호출한 위치로 되돌아간다.
■ 매개변수(parameter)는 함수에 입력으로 전달되는 값을 받는 변수, 인수(argument)는 함수를 호출할 때 전달하는 입력값을 의미한다.
■ 예를 들어, 다음과 같이 두 개의 값을 더하는 add 함수가 있다고 했을 때,
def add(a, b): # a, b는 매개변수
result = a + b
return result
result = add(2, 3) # 2, 3은 인수
print(result)
```#결과#```
5
````````````
■ result = add(2, 3)으로 add 함수를 호출하면, add 함수 내의 명령문들이 실행된다. 이때 인자는 정수 2와 3이다.
■ 인자인 정수 2와 3은 차례대로 add 함수의 매개변수 a와 b에 들어간다. 그러므로 add 함수 내의 명령문인 result = a(2) + b(3)이 되어 result의 값은 5가 된다. 최종적으로 result(5)를 return 키워드로 반환되고, add 함수는 종료된다.
■ 그다음, 호출한 위치로 되돌아가서 함수 외부에 있는 result 변수에는 add(2, 3)의 결과인 5가 할당된다.
■ 이러한 함수는 작성되면 몇 번이라도 호출할 수 있다. 이것이 함수의 가장 큰 장점이다.
■ 예를 들어, 덧셈 계산이 3번 필요하다면 다음과 같이 add() 함수를 3번 호출하면 된다.
print(add(1, 10))
print(add(3, -2))
print(add(100, 1000))
```#결과#```
11
1
1100
````````````
result = (add(1, 10) - add(3, -2)) * add(100, 1000)
print(result)
```#결과#```
11000
````````````
■ 함수 정의 및 명령문의 순서에 주의해야 한다. 파이썬 인터프리터는 소스 코드를 한 줄씩 읽는다. 함수 안의 명령문은 함수가 호출될 때까지 실행되지 않으며, 함수 외부에 있는 문장은 즉시 실행된다. 그러므로 함수를 호출하기 전에 반드시 함수를 먼저 정의해야 한다.
■ 만약, 다음과 같이 함수를 정의하기 전에 함수를 호출하면 해당 함수가 없다는 오류가 발생할 것이다.
flag = isVowel('Every')
print(flag)
def isVowel(word):
word = word.upper()
vowels = ['A', 'E', 'I', 'O', 'U']
for vowel in vowels:
if vowel not in word:
return False
return True
■ 그러나 다음과 같이 함수 내에서 아직 정의되지 않은 함수를 호출할 수는 있다.
def main():
flag = isVowel('Every')
print(flag)
def isVowel(word):
word = word.upper()
vowels = ['A', 'E', 'I', 'O', 'U']
for vowel in vowels:
if vowel not in word:
return False
return True
main()
```#결과#```
False
````````````
■ isVowel() 함수가 main() 함수 뒤에 정의되어 있어도 isVowel() 함수는 main() 함수 내에서 호출된다.
■ 먼저, 파이썬 인터프리터는 순서대로 main() 함수와 isVowel() 함수를 읽게 된다.
■ 그러므로 마지막 줄에서 main() 함수를 호출하면, main() 함수 내의 문장들이 실행되고 isVowel() 함수가 문제 없이 호출될 수 있다. main() 함수가 호출되는 시점은 이미 파이썬 인터프리터가 isVowel() 함수를 읽은 후의 시점이기 때문이다.
1.4 인자(argument)를 매개변수(parameter)로 전달하는 방법
■ 인수(argument)는 함수 호출 시 함수 헤더의 매개변수(parameter)에 전달되는 값, 매개변수는 이 값을 전달받는 변수이다. ■ 매개변수의 개수는 인수의 개수와 정확히 일치해야 한다.
■ 인자(또는 인수)를 파라미터로 전달하는 방법에는 위치 전달(pass by position), 키워드 전달(pass by keyword), 기본값 전달(pass by defalut value)이 있다.
1.4.1 위치 인자(positional argument)
■ 기본 인수 전달 방식이다. 위치 인자는 매개변수 위치와 일치시키는 인자를 위치 인자라고 부른다.
■ 함수 호출 시 전달되는 인자의 순서는 함수 헤더에 정의된 파라미터의 순서와 일치해야 한다.
■ 즉, 첫 번째 인자는 첫 번째 파라미터에, 두 번째 인자는 두 번째 파라미터에, 세 번째 인자는 세 번째 파라미터에 전달되는 식이다.
def add(a, b):
result = a + b
return result
add(2, 3) # 2는 매개변수 a에, 3은 매개변수 b에 전달된다.
1.4.2 키워드 인자(keyword argument)
■ 키워드 인자는 인자들 앞에 키워드를 두어서 인자들을 구분하여 인자를 전달하는 방법이다.
- 매개변수의 이름을 명시적으로 지정해서 값을 매개변수에 전달할 수 있다.
■ 이 방법의 장점은 인수의 위치가 매개변수의 위치와 달라도 된다는 점이다. 즉, 키워드 인수를 사용한다면 다음과 같이 인자들이 어떤 순서로 전달되어도 상관없다.
def sub(x, y, z):
return x - y - z
sub(z = 1, y = -2, x = 5)
```#결과#```
6
````````````
1.4.3 기본값 인자(default argument)
■ 함수 정의 시 매개변수에 기본값을 지정하여, 함수 호출 시 인자를 생략할 수 있다. 즉, 함수의 매개변수가 기본값을 가질 수 있다. 이것을 기본값(디폴트) 인자라고 한다.
■ 예를 들어, 다음과 같은 greet() 함수에서 매개변수 msg에 기본값으로 'hello'를 설정하였다. 이것이 기본값 인자이다. 이렇게 하면 greet() 함수 호출 시, 매개변수 msg에 인자를 전달하지 않아도 된다.
def greet(name, msg='Hello'):
print(msg, name)
greet('John')
```#결과#```
Hello John
````````````
■ 위와 같이 위치 인자와 기본값 인자를 같이 사용할 수 있다. 단, 반드시 기본값 인자는 위치 인자 뒤에 위치해야 한다.
def greet(msg='Hello', name):
print(msg, name)
```#결과#```
SyntaxError: non-default argument follows default argument
````````````
■ 키워드 인자와 위치 인자를 같이 사용할 경우에도 위치 인자는 키워드 인자 앞에 나와야 한다.
def sub(x, y, z):
return x - y - z
sub(z = 1, 2, 5)
```#결과#```
SyntaxError: positional argument follows keyword argument
````````````
sub(1, 2, z=5)
```#결과#```
-6
````````````
■ 위치 인자는 매개변수 위치와 순서대로 매핑되기 때문이다.
1.4.4 가변 인자
■ 매개변수로 *와 **을 이용하여 임의의 개수의 인자를 받을 수 있다. *는 위치 인자를 **는 키워드 인자를 가변 인자로 받는다.
■ 예를 들어 다음과 같이 매개변수 이름 앞에 *을 사용하여 가변 길이 인자를 함수에 전달할 수 있다.
def func1(*args):
return args
func1(1)
```#결과#```
(1,)
````````````
result = func1(1)
type(result)
```#결과#```
tuple
````````````
func1(1, (2, 3), '4', 5, [6, 7], 8)
```#결과#```
(1, (2, 3), '4', 5, [6, 7], 8)
````````````
def func3(*args, x):
return args, x
a, b = func3(1, 2, 3, 4, 5, x = 'A')
print(a); print(b)
```#결과#```
(1, 2, 3, 4, 5)
A
````````````
- func3에서 args와 x라는 두 개의 값을 반환하므로, 함수 호출 부분에서 두 개의 반환값을 두 개의 변수에 받아주면 된다.
■ *args 매개변수가 위치 인자들을 튜플 형태로 받아주는 것을 볼 수 있다. 그래서 반환 결과가 튜플이 나오는 것이다.
■ 매개변수 이름 앞에 **를 사용하여 가변 길이 키워드 인자를 나타낸다. 인자는 딕셔너리 형태로 전달된다.
def func2(**kwargs):
return kwargs
func2(age = 30, name = 'John', c = 2)
```#결과#```
{'age': 30, 'name': 'John', 'c': 2}
````````````
■ 위의 **kwargs처럼 매개변수 이름 앞에 **을 붙이면 함수 외부의 Key=Value 형태의 입력값은 함수를 호출하면, 함수 내의 딕셔너리 kwargs에 저장되는 것을 볼 수 있다.
1.5 입력값과 리턴값에 따른 함수의 형태
■ 다음과 같이 입력값과 반환값이 있는 함수가 일반적인 형태의 함수 정의이다.
def 함수이름(매개변수1, 매개변수2, ...):
명령문1
명령문2
...
return 반환값
■ 함수 정의에서 파라미터와 return 명령은 선택사항이다.
1.5.1 리턴값이 없는 함수
■ 값을 반환하지 않는 함수는 return 명령을 포함하지 않는 함수를 말한다.
def msg(color):
print('Hello')
print(f'This color is {color}.')
msg('black')
```#결과#```
Hello
This color is black.
````````````
■ 위와 같은 함수는 사실 None 값을 반환하지만, 해당 값으로 수행할 수 있는 작업은 없다.
result = msg('black')
```#결과#```
Hello
This color is black.
````````````
print(result)
```#결과#```
None
````````````
- result에 None 값이 할당된 것을 볼 수 있다.
- None을 리턴한다는 것은 리턴값이 없다는 것이다.
1.5.2 입력값이 없는 함수
■ 입력값이 없는 함수도 존재할 수 있다.
def msg2():
return 'white'
msg2()
```#결과#```
'white'
````````````
result = msg2()
print(result)
```#결과#```
white
````````````
1.5.3 입력값 & 리턴값이 없는 함수
■ 1.3의 예시 중 main() 함수는 매개변수와 return 명령이 없는 함수이다.
def main():
flag = isVowel('Every')
print(flag)
def isVowel(word):
word = word.upper()
vowels = ['A', 'E', 'I', 'O', 'U']
for vowel in vowels:
if vowel not in word:
return False
return True
main()
```#결과#```
False
````````````
■ 이러한 형식은 마지막 줄에서 main() 함수를 호출하여, 실행될 작업을 초기화한다.
■ 예를 들어, msg1, msg2, msg3라는 3개의 함수가 있을 때, 이 3개의 함수를 위와 같은 형식으로 main() 함수 내에 넣으면 가독성이 좋아진다.
def msg1():
...
def msg2():
...
def msg3():
...
def main():
...
msg1()
msg2()
msg3()
...
main()
■ 이러한 방법을 구조화 프로그래밍이라고도 한다. 구조화 프로그래밍은 큰 작업을 계속 나누어 설계하는 기법이며, 나누어진 부분이 너무 간단해서 함수로 구현될 수 있을 때까지 나눈다.
■ 즉, 각 함수들은 특징적인 한 가지 기능만을 수행할 때까지 나누는 것이다. 하나의 함수가 여러 작업을 하면 안 된다.
1.6 순환(recursion)
■ 순환은 어떤 알고리즘이나 함수가 자기 자신을 호출하여 문제를 해결하는 프로그래밍 기법이다.
■ 순환은 본질적으로 정수의 팩토리얼과 같이 순환적인 문제에 적합하다.
\( n! =
\begin{cases}
1 & \text{if } n = 0 \\
n \cdot (n-1)! & \text{if } n \geq 1
\end{cases} \)
■ 팩토리얼 \( n! \)을 정의하는데 다시 팩토리얼 \( \left( n-1 \right) ! \)이 사용되는 것을 볼 수 있다. 이러한 정의를 순환적인 정의라고 한다.
def factorial(n):
if n==1:
return 1
else:
return n*factorial(n-1)
- factorial 함수는 n의 값이 1이 아니라면 else 문이 실행되어 n*factorial(n-1)이 수행된다. 이때, 다시 factorial 함수가 호출된다. 이 과정이 반복되어 펙토리얼 값을 계산할 수 있는 것이다.
1.7 변수의 범위
■ 함수 내에서 생성된 변수는 해당 함수 내에서 명령으로 접근할 수 있지만, 해당 함수의 실행이 끝날 때 존재하지 않게 된다. 함수 내의 변수는 해당 함수가 호출될 때마다 재생성되는 것으로 볼 수 있다.
■ 예를 들어 다음과 같이 함수 내에 변수 x가 있고 함수 외부에 변수 x가 있다고 했을 때
def func():
x = 10
print(x)
x = 20
func()
```#결과#```
10
````````````
print(x)
```#결과#```
20
````````````
■ 함수 안에서 생성되는 변수를 지역 변수(local variable)라고 하며, 지역 변수는 함수가 종료되면 사라진다. 그러므로 지역 변수를 함수 외부에서 사용하려고 하면 오류가 발생한다.
■ 반면, 함수 외부에서 생성된 변수는 어디에서나 사용할 수 있다. 이러한 변수를 전역 변수(global variable)라고 한다. 즉, 모든 함수는 다음 예시처럼 전역 변수의 값을 사용할 수 있다.
def func(x):
print(x)
a = 30 # 전역 변수
func(a) # 함수 호출
```#결과#```
30
````````````
■ 위의 예시처럼 지역 변수와 전역 변수가 동일한 변수명을 갖는다고 해도, 두 변수는 서로 어떠한 관계도 갖고 있지 않다. 즉, 완전히 다른 변수로 처리된다.
1.7.1 global 키워드
■ 함수 안에서 전역 변수의 값을 변경하고 싶은 경우 global이라는 키워드를 사용하면 된다.
def func():
global x
print(x)
x = 20
func()
```#결과#```
20
````````````
print(x)
```#결과#```
20
````````````
'파이썬' 카테고리의 다른 글
클로저와 데코레이터, 이터레이터와 제너레이터, 파이썬 타입 어노테이션 (0) | 2024.09.09 |
---|---|
내장 함수, 정렬과 탐색, 람다식 (1) | 2024.09.07 |
파일 읽고 쓰기 (0) | 2024.09.03 |
제어문(control statement) (2) - 반복문 for, while (0) | 2024.09.02 |
제어문(control statement) (1) - 조건문 if (0) | 2024.08.28 |