5. 함수
정의
def 함수명(인수 1, 인수 2, ...) :
명령 1
명령 2
. . .
명령 n
return value
일부 요소들은 생략 가능하다.
- 인수가 없다면 생략할 수 있다.
- 반환값이 필요 없으면 return을 생략할 수 있다.
기본값 지정
특정 인수에 기본값을 지정해줄 수도 있다. 매개변수 선언 시 매개변수 = 기본값
의 형태로 작성한다.
def plus(a = 0, b = 0) :
return a + b
함수 호출 시 해당 매개변수의 입력을 생략하면 자동으로 기본값으로 설정된다.
중요한 점은, 기본값을 갖는 매개변수들은 무조건 뒤쪽에 배치되어야 한다.
뇌피셜 : 기본값이 지정된 매개변수가 중간에 선언된 경우, 실제 함수 호출 시 입력되는 값들이 어떤 매개변수에 전달되어야 하는지를 명확하게 알 수 없는 문제가 발생하기 때문이 아닐까?
즉, ① def sum(a = 0, b)
는 불가능하고, ② def sum(a, b = 0)
은 가능하다. sum(4)
를 실행하는 경우 ①번은 4라는 값이 a, b 중 어디에 전달되어야 하는지 명확하게 알 수 없기 때문이다.
람다 함수
def
예약어를 사용한 함수 정의 외에도 간단하게 함수를 정의해서 사용하는 방법도 있다.
>>> lambda 인수 1, 인수 2, ... : 반환값
이름이 없으므로 직접적인 호출이 불가능하다. 따라서 변수에 대입하고 C의 함수 포인터처럼 사용할 수 있다.
>>> plus = lambda x, y : x + y
>>> plus(4, 2)
6
하지만 이렇게 사용하면 그냥 def
와 별다른 차이가 없으므로, lambda 함수는 인자로 ‘함수’를 입력받는 함수들에 일회용으로 사용되곤 한다.
map()
map()
함수는 리스트나 튜플의 전체 요소에 대해 특정한 처리를 하고 싶을 때 사용한다.
map(처리를 하는 함수, 처리 대상 리스트/튜플)
의 방식으로 사용한다.
def
와 lambda
2가지를 사용하여 리스트의 값을 제곱하는 예시를 살펴보자.
>>> def square(x) :
return x * x
>>> list(map(square, [1, 2, 3]))
[1, 4, 9]
map()
함수는 map
객체를 반환하므로 list()
함수를 사용하여 형변환을 해 줌으로써 결과로서 리스트를 얻는다.
다음은 lambda
를 사용한 경우이다.
>>> list(map(lambda x : x * x, [1, 2, 3]))
[1, 4, 9]
map()
함수 밖에서 사용하고자 하는 간단한 기능의 함수를 정의할 필요 없이, map()
함수 호출 시에 간단하게 원하는 기능을 함수로서 만들고자 할 때 lambda
함수를 사용할 수 있다.
이처럼 lambda
함수를 사용해 인자로 함수를 입력받는 함수들의 종류는 매우 다양하다.
filter()
조건에 맞는 요소만 추출하는 함수이다.
>>> list(filter(lambda a : a % 2 == 0, [0, 1, 2, 3, 4, 5]))
[0, 2, 4]
sorted()
sorted()
는 전달된 리스트를 직접 정렬하지 않고, 요소들이 정렬된 또 다른 리스트를 반환하는 함수이다.
labmda
함수를 인자로 전달해 key = lambda . . .
형태로 정렬 기준을 지정해줄 수 있다.
>>> sorted(["watermelon", "mango", "grapefruit", "banana"])
['banana', 'grapefruit', 'mango', 'watermelon']
>>> sorted(["watermelon", "mango", "grapefruit", "banana"], key = lambda x : len(x))
['mango', 'banana', 'watermelon', 'grapefruit']
리스트 각 요소의 길이를 기준으로 정렬된 결과 리스트가 반환된 것을 볼 수 있다.
reverse=True
인자를 입력하는 경우 정렬 순서를 반대로 설정할 수 있다.
>>> sorted(["watermelon", "mango", "grapefruit", "banana"], key = lambda x : len(x), reverse=True)
['watermelon', 'grapefruit', 'banana', 'mango']
튜플의 두 번째 요소를 기준으로 정렬하는 등의 활용도 가능하다.
>>> sorted([(0, 4), (6, 2), (3, 1)])
[(0, 4), (3, 1), (6, 2)] // 0 → 3 → 6
>>> sorted([(0, 4), (6, 2), (3, 1)], key = lambda x : x[1])
[(3, 1), (6, 2), (0, 4)] // 1 → 2 → 4
리스트 내포 표기법 (List comprehension)
리스트에 저장할 요소의 규칙 등을 직관적으로 지정하여 리스트를 생성할 수 있다.
[식 for 요소명 in 리스트]
의 형태로 사용한다. 예시를 살펴보자.
>>> [x * 2 for x in [1, 2, 3, 4]]
[2, 4, 6, 8]
>>> [x * x for x in range(5)]
[0, 1, 4, 9, 16]
- 이를 활용해 간단하게 2차원 배열을 만들 수도 있다.
>>> [[x, x * 2, x * 3] for x in [1, 2, 3]]
[[1, 2, 3], [2, 4, 6], [3, 6, 9]]
- 특정 값으로 초기화되는 다차원 리스트의 생성도
range()
와 함께 활용하여 간단하게 해결할 수 있다.
>>> [[0 for x in range(3)] for y in range(4)]
[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
- 바로 위의 예시에서는
x
와y
가 큰 의미가 없지만, 이를 살짝 변형하여 1씩 증가하는 값을 저장할 수도 있다.
y
를 통해 만든 4칸의 리스트의 각 요소에 x
를 통해 만든 3칸의 리스트를 각각 넣고 있다. 따라서 y
순번에 3을 곱한 후, x
의 순번을 더해 0부터 11까지 값이 차례대로 증가하는 다차원 리스트를 만들 수 있다.
>>> [[x + y * 3 for x in range(3)] for y in range(4)]
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]
- 이미 만들어진 2차원 배열에 접근하여 값을 변경하는 것도 가능하다.
>>> data = [[x + y * 3 for x in range(3)] for y in range(4)]
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]
>>> [[x * x for x in row] for row in data]
[[0, 1, 4], [9, 16, 25], [36, 49, 64], [81, 100, 121]]
- 조건식을 추가하여
filter()
처럼 사용할 수도 있다.
[식 for 요소명 in 리스트 if 조건식]
의 형태로 사용한다. 조건을 충족한 요소에 대해서만 처음 식이 실행된다.
>>> [x for x in [0, 1, 2, 3, 4, 5] if x % 2 == 0]
[0, 2, 4]
[0, 1, 2, 3, 4, 5]
중에서 x % 2 == 0
을 충족시키는 값만 x
를 통해 리스트에 저장되는 것을 볼 수 있다.
6. 모듈
파이썬의 모듈은 C의 라이브러리와 같은 역할을 한다. 필요한 모든 함수나 자료구조를 전부 만들어 쓰는 것은 생산적이지 못하기 때문에, 기존에 정의된 모듈 혹은 사용자 정의 모듈을 가져와서(import) 사용할 수 있다.
임포트(Import)
특정 모듈을 사용하려면 해당 모듈을 가져와야, 즉, 임포트(Import)해야 한다. 예를 들어, 난수를 생성하려면 random 모듈을 임포트(Import)해야 한다.
import random
print(random.randint(0, 5))
위 예시의 randint()
함수는 random 모듈을 임포트하지 않으면 사용할 수 없다.
수많은 모듈과 각 모듈에 포함된 다양한 함수가 있지만, 이 단락에서 모두 소개하지 않겠고, 할 수도 없다. (그리고 뇌피셜이지만, 그때그때 필요하면 찾아보고 쓰는 게 코딩을 잘 하는 게 아닐까?)
필요한 것만 임포트하기
전체 모듈을 임포트하는 대신, 해당 모듈에서 필요한 함수만 임포트할 수도 있다.
from 모듈명 import 함수명
의 형태로 임포트한다.
from random import randint
print(random.randint(0, 5))
당연하겠지만 이 경우 randint()
를 제외한 random 모듈 내의 다른 함수들은 사용이 불가능하다.
__name__
모듈은 누군가 작성한 파일을 내가 빌려 와서 사용하는 형태이다. 따라서 내가 작성한 파일도 모듈이 될 수 있다.
__name__
변수를 이용하면 어떤 파일이 실행될 때 내가 임포트를 했는지, 아니면 임포트를 당했는지를 알 수 있다.
__name__
변수는 다음 값을 갖는다.
- 프로그램이 시작된 파일에서는
__name__
변수의 값이__main__
으로 설정된다. - 모듈로서 임포트된 파일에서는
__name__
변수의 값이 해당 모듈명으로 설정된다.
따라서 모듈로서 호출된 경우를 구분하기 위해 주로 다음과 같은 형식의 코드를 주로 사용한다.
import ...
초기화 코드
함수 및 클래스 정의, 전역 변수 선언 등
. . .
if __name__ == '__main__' :
. . .
__name__
변수에 저장된 값이 __main__
인 경우 해당 파일에서 프로그램이 시작된 것이므로 특별한 기능을 수행하도록 설정할 수 있다.
여기까지 함수와, 함수를 모아 둔 파일인 모듈을 살펴보았다.
앞서 언급했든 모듈엔 수많은 종류가 있으므로, 추후 다른 내용을 학습하며 새로운 모듈이 나오면 그 때 해당 모듈에 포함된 함수들을 살펴보도록 하겠다.
다음 글에서는 게임 개발에 필요한 PyGame 모듈에 대해 다뤄볼 예정이다.
(다음 글에서 계속됩니다.)