Python 강좌 : 제 20강 - 사용자 정의 함수
사용자 정의 함수(User-defined Functions)
함수(Function)는 입력을 받아 정해진 작업을 수행하고 결과를 돌려주는 코드 묶음입니다.
지금까지 사용한 print(), len(), max()처럼 Python이 미리 만들어 둔 함수를 내장 함수(Built-in Functions)라 하며, 개발자가 직접 정의한 함수를 사용자 정의 함수(User-defined Functions)라 합니다.
사용자 정의 함수(User-defined Functions)는 코드를 구조적으로 설계할 수 있게 하는 역할을 합니다.
이를 통해 코드의 구조화(structuring), 모듈화(modularization), 재사용성(reusable), 가독성(Readability) 등을 높일 수 있습니다.
Python에서는 def와 return을 사용하여 사용자 정의 함수를 생성할 수 있습니다.
동일한 작업이 반복되는 경우에도 사용자 정의 함수를 호출하여 사용할 수 있습니다.
같은 계산을 코드 여러 곳에 복사해 두면, 계산 방식이 바뀌었을 때 모든 곳을 찾아 고쳐야 하며 한 곳이라도 빠뜨리면 오류가 됩니다. 함수로 묶어 두면 한 곳만 고치면 함수를 호출하는 모든 곳에 반영됩니다.
이번 강좌에서는 함수를 정의하고 호출하는 방법, 인수가 매개변수에 대응되는 규칙, 반환값이 호출한 곳으로 돌아가는 흐름, 함수 안과 밖의 변수 범위(scope), 그리고 함수를 값처럼 다루는 일급 함수까지 알아봅니다.
- Tip : 동일한 작업이 반복되지 않더라도, 코드의
가독성이나모듈화등을 위해 사용자 정의 함수를 사용합니다.
함수 생성
함수는 정의(definition)와 호출(call) 두 단계로 사용합니다.
def 문으로 함수를 정의하면 Python은 함수 본문을 바로 실행하지 않고, 함수 객체를 만들어 함수명에 저장만 해 둡니다. 본문은 함수를 호출할 때마다 실행됩니다.
def add(a, b):
return a + b
print(add(3, 4))
print(add([1, 2, 3], [4, 5, 6]))
print(add("YUN", "DAEHEE"))- 결과
- 7
[1, 2, 3, 4, 5, 6]
YUNDAEHEE
def 함수명(매개변수1, 매개변수2, ....):를 사용하여 사용자 정의 함수를 선언할 수 있습니다.
함수명으로 사용자 정의 함수를 호출합니다.
함수 안에서 사용될 값을 받을 이름을 매개변수(Parameter)로 선언하며, 함수를 호출할 때 실제로 전달하는 값을 인수(Argument)라 합니다.
return 반환값을 사용하여 반환값이 결과로 반환됩니다. return을 작성하지 않으면 None이 반환됩니다.
첫 번째 줄의 add(3, 4)가 실행되는 순서는 다음과 같습니다.
3이 매개변수a에,4가 매개변수b에 저장됩니다.- 함수 본문의
return a + b가 실행되어7이 계산됩니다. - 함수가 종료되고, 호출한 자리인
add(3, 4)가 반환값7로 바뀝니다. - 결과적으로
print(7)이 실행되어7이 출력됩니다.
두 번째와 세 번째 줄은 같은 함수에 List와 문자열을 전달했으므로 + 연산이 각각 리스트 연결과 문자열 연결로 동작합니다.
- Tip : 매개변수의 자료형을 제한하지 않으므로
+연산이 가능한정수,List,문자열등을 모두 사용할 수 있습니다. 반대로add(1, "A")처럼+가 불가능한 조합을 전달하면 함수 안에서TypeError가 발생합니다.
def add(a, b):
"""두 값을 더한 결과를 반환합니다."""
return a + b
print(add.__doc__)- 결과
- 두 값을 더한 결과를 반환합니다.
함수 본문의 첫 줄에 작성한 문자열은 독스트링(docstring)이 되어 함수의 설명으로 저장됩니다.
함수명.__doc__이나 help(함수명)으로 설명을 확인할 수 있으므로, 함수가 무엇을 받아 무엇을 반환하는지 적어 두면 나중에 함수를 사용하는 사람이 본문을 읽지 않아도 됩니다.
인수와 매개변수의 대응
함수를 호출하면 Python은 전달된 인수를 정해진 규칙에 따라 매개변수에 하나씩 짝지어 줍니다.
인수를 순서대로 전달하는 위치 인수(Positional arguments)와 매개변수 이름을 지정해 전달하는 키워드 인수(Keyword arguments) 두 가지 방식이 있으며, 두 방식을 섞어서 사용할 수도 있습니다.
def info(name, age, city="Seoul"):
return f"{name} / {age} / {city}"
print(info("YUN", 20))
print(info(age=20, name="YUN"))
print(info("YUN", city="Busan", age=30))- 결과
- YUN / 20 / Seoul
YUN / 20 / Seoul
YUN / 30 / Busan
첫 번째 호출은 위치 인수만 사용했으므로 "YUN"은 첫 번째 매개변수 name에, 20은 두 번째 매개변수 age에 대응됩니다. city는 전달되지 않았으므로 기본값 "Seoul"이 사용됩니다.
두 번째 호출은 키워드 인수만 사용했으므로 순서와 관계없이 이름이 같은 매개변수에 대응됩니다.
세 번째 호출은 "YUN"이 위치로 name에 먼저 대응되고, 나머지 city와 age는 이름으로 대응됩니다.
Python은 다음 순서로 인수를 대응시킵니다.
- 위치 인수를 왼쪽부터 차례대로 매개변수에 채웁니다.
- 키워드 인수를 이름이 같은 매개변수에 채웁니다.
- 아직 비어 있는 매개변수에 기본값을 채웁니다.
- 그래도 비어 있는 매개변수가 있거나, 하나의 매개변수가 두 번 채워지면
TypeError가 발생합니다.
def info(name, age, city="Seoul"):
return f"{name} / {age} / {city}"
try:
info("YUN")
except TypeError as e:
print(e)
try:
info("YUN", 20, name="KIM")
except TypeError as e:
print(e)
try:
info("YUN", 20, country="KR")
except TypeError as e:
print(e)- 결과
- info() missing 1 required positional argument: ‘age’
info() got multiple values for argument ‘name’
info() got an unexpected keyword argument ‘country’
첫 번째 호출은 기본값이 없는 age가 채워지지 않아 오류가 발생합니다.
두 번째 호출은 "YUN"이 위치로 name에 이미 대응되었는데 name="KIM"으로 한 번 더 전달했으므로 같은 매개변수에 값이 두 번 전달되었다는 오류가 발생합니다.
세 번째 호출은 함수에 존재하지 않는 매개변수 이름 country를 사용했으므로 오류가 발생합니다.
오류 메시지에 어떤 매개변수가 문제인지 표시되므로, 함수 호출 오류가 발생하면 메시지의 매개변수 이름부터 확인합니다.
- Tip : 위치 인수는 키워드 인수보다 앞에 작성해야 합니다.
info(name="YUN", 20)처럼 작성하면 실행 전에SyntaxError: positional argument follows keyword argument가 발생합니다.
def info(name, age, city="Seoul"):
return f"{name} / {age} / {city}"
args = ("YUN", 20)
kwargs = {"city": "Busan"}
print(info(*args))
print(info(*args, **kwargs))- 결과
- YUN / 20 / Seoul
YUN / 20 / Busan
함수를 호출할 때 *를 붙이면 Tuple이나 List의 원소를 풀어서 위치 인수로 전달하고, **를 붙이면 Dict의 키=값을 풀어서 키워드 인수로 전달합니다.
즉, info(*args, **kwargs)는 info("YUN", 20, city="Busan")과 같습니다.
이미 목록이나 사전에 담겨 있는 값을 함수에 넘길 때 원소를 하나씩 꺼내 쓰지 않아도 됩니다.
반환값의 흐름
return은 두 가지 일을 동시에 합니다. 값을 호출한 곳으로 돌려주고, 함수를 그 자리에서 즉시 종료합니다.
그러므로, return 아래에 작성된 코드는 실행되지 않으며, 조건에 따라 여러 개의 return을 두어 상황마다 다른 값을 반환할 수 있습니다.
def sign(x):
if x > 0:
return "양수"
if x < 0:
return "음수"
print("0은 양수도 음수도 아닙니다")
print(sign(5))
print(sign(-3))
result = sign(0)
print(result)- 결과
- 양수
음수
0은 양수도 음수도 아닙니다
None
sign(5)는 첫 번째 if에서 return "양수"를 만나 즉시 종료되므로, 아래의 두 번째 if는 검사하지 않습니다.
sign(-3)은 첫 번째 조건이 거짓이므로 다음 줄로 진행하여 return "음수"에서 종료됩니다.
sign(0)은 두 조건이 모두 거짓이므로 print()까지 실행된 뒤, return을 만나지 못한 채 함수의 끝에 도달합니다. 이 경우 함수는 자동으로 None을 반환하므로 result에는 None이 저장됩니다.
def square(x):
return x * x
def show(x):
print(x * x)
a = square(3) + 1
print(a)
b = show(3)
print(b)- 결과
- 10
9
None
초보자가 가장 자주 혼동하는 부분은 print와 return의 차이입니다.
square(3)은 값 9를 돌려주므로 square(3) + 1처럼 다른 계산에 이어서 사용할 수 있습니다.
show(3)은 9를 화면에 출력할 뿐 돌려주는 값이 없으므로, b에는 None이 저장됩니다. 두 번째 줄의 9는 show(3)이 실행되면서 출력한 값이고, 세 번째 줄의 None이 b에 저장된 값입니다.
- Tip : 계산 결과를 다른 곳에서 사용해야 한다면
print가 아닌return으로 돌려줍니다. 출력은 함수를 호출한 쪽에서 필요할 때 하는 것이 재사용에 유리합니다.
기본값 설정
매개변수에 기본값을 지정하면 자주 쓰는 값은 생략하고, 필요할 때만 다른 값을 전달할 수 있습니다.
def add(a, b=10):
return a + b
print(add(1))
print(add(1, 2))- 결과
- 11
3
매개변수에 기본값을 할당할 수 있습니다.
기본값이 할당되면 함수를 호출할 때 매개변수를 채우지 않으면 기본값으로 사용합니다.
add(1)은 b가 전달되지 않았으므로 1 + 10이 되고, add(1, 2)는 전달된 2가 기본값보다 우선하므로 1 + 2가 됩니다.
기본값이 있는 매개변수는 기본값이 없는 매개변수보다 뒤에 선언해야 합니다.
def add(a=10, b):처럼 작성하면 위치 인수 하나만 전달했을 때 어느 매개변수에 대응할지 모호해지므로 SyntaxError가 발생합니다.
def append_bad(x, L=[]):
L.append(x)
return L
def append_good(x, L=None):
if L is None:
L = []
L.append(x)
return L
print(append_bad(1))
print(append_bad(2))
print(append_good(1))
print(append_good(2))- 결과
- [1]
[1, 2]
[1]
[2]
기본값은 함수가 정의될 때 한 번만 생성됩니다.
그러므로, List처럼 변경 가능한(mutable) 객체를 기본값으로 사용하면 호출할 때마다 같은 객체가 계속 공유되어 값이 누적됩니다.
append_bad(2)의 결과가 [2]가 아닌 [1, 2]인 이유는, 두 번의 호출이 같은 리스트 하나를 기본값으로 사용했기 때문입니다.
append_good은 기본값으로 변경 불가능한 None을 두고, 호출될 때마다 함수 안에서 []를 새로 만들므로 호출끼리 값이 섞이지 않습니다.
def append_bad(x, L=[]):
L.append(x)
return L
print(append_bad.__defaults__)
append_bad(1)
print(append_bad.__defaults__)- 결과
- ([],)
([1],)
함수의 기본값은 함수명.__defaults__에 Tuple로 저장되어 있습니다.
append_bad(1)을 한 번 호출한 뒤 기본값 자체가 [1]로 바뀐 것을 확인할 수 있습니다. 즉, 기본값 리스트는 함수 객체에 붙어서 함수가 존재하는 동안 계속 유지됩니다.
- Tip : 변경 가능한 객체를 기본값으로 사용해야 한다면
None을 기본값으로 두고 함수 안에서 새로 생성합니다.
다중 입력
print()가 인수를 몇 개 전달하든 모두 출력하는 것처럼, 인수의 개수를 미리 정할 수 없는 함수가 필요할 때가 있습니다.
def add(*num):
result = 0
for i in num:
result += i
return result
print(add(1, 2, 3))- 결과
- 6
매개변수에 *를 입력할 경우 개수를 지정하지 않고 인수를 전달받을 수 있습니다.
이를 가변 위치 인수(Variable positional arguments)라 하며, 전달된 인수들은 Tuple로 묶여 num에 저장됩니다.
관례적으로 *args라는 이름을 많이 사용합니다.
def add(*num):
print(type(num), num)
return sum(num)
print(add())
print(add(1, 2, 3))
values = [4, 5, 6]
print(add(*values))- 결과
- <class ‘tuple’> ()
0
<class ‘tuple’> (1, 2, 3)
6
<class ‘tuple’> (4, 5, 6)
15
인수를 하나도 전달하지 않으면 num은 빈 Tuple ()이 되므로, 함수 안에서 빈 경우도 처리할 수 있도록 작성합니다.
이미 List에 담긴 값을 전달하려면 add(values)가 아닌 add(*values)로 풀어서 전달합니다. add(values)로 전달하면 num은 ([4, 5, 6],)처럼 리스트 하나를 원소로 가진 Tuple이 됩니다.
정의할 때의 *는 인수를 묶고, 호출할 때의 *는 인수를 푼다는 점에서 서로 반대 방향으로 동작합니다.
키워드 인수
def profile(name, *args, **kwargs):
print(name, args, kwargs)
profile("YUN", 1, 2, age=20, city="Seoul")- 결과
- YUN (1, 2) {‘age’: 20, ‘city’: ‘Seoul’}
매개변수=값의 형태로 이름을 지정하여 전달하는 인수를 키워드 인수(Keyword arguments)라 합니다.
매개변수에 **를 입력할 경우 정의되지 않은 키워드 인수들을 Dict로 묶어 전달받을 수 있으며, 관례적으로 **kwargs라는 이름을 사용합니다.
매개변수는 일반 매개변수, *args, **kwargs 순서로 선언합니다.
예제에서 "YUN"은 첫 번째 위치 인수이므로 name에 대응되고, 남은 위치 인수 1, 2는 args에 Tuple로, 이름이 정의되지 않은 age, city는 kwargs에 Dict로 모입니다.
def profile(name, **kwargs):
for key, value in kwargs.items():
print(name, key, value)
profile("YUN", age=20, city="Seoul")- 결과
- YUN age 20
YUN city Seoul
kwargs는 일반 사전이므로 items()로 키와 값을 함께 반복할 수 있습니다.
**kwargs는 설정 옵션처럼 어떤 이름이 들어올지 미리 정하기 어려운 값을 받을 때 유용합니다. 단, 매개변수 이름이 드러나지 않아 오타를 내도 오류가 발생하지 않으므로, 받을 이름이 정해져 있다면 일반 매개변수로 선언하는 것이 안전합니다.
위치 전용 / 키워드 전용 매개변수
def calc(a, b, /, c, *, d):
return a + b + c + d
print(calc(1, 2, 3, d=4))
print(calc(1, 2, c=3, d=4))
try:
calc(a=1, b=2, c=3, d=4)
except TypeError as e:
print(e)
try:
calc(1, 2, 3, 4)
except TypeError as e:
print(e)- 결과
- 10
10
calc() got some positional-only arguments passed as keyword arguments: ‘a, b’
calc() takes 3 positional arguments but 4 were given
매개변수 목록에 /를 작성하면 앞쪽 매개변수는 위치로만 전달해야 합니다. (Python 3.8 이상)
매개변수 목록에 *를 작성하면 뒤쪽 매개변수는 키워드로만 전달해야 합니다.
/와 * 사이의 매개변수(c)는 위치와 키워드 중 어느 방식으로도 전달할 수 있습니다.
세 번째 호출은 위치 전용인 a, b를 이름으로 전달했으므로 오류가 발생하고, 네 번째 호출은 키워드 전용인 d를 위치로 전달하여 위치 인수가 하나 많아졌으므로 오류가 발생합니다.
- Tip : 위치 전용 매개변수는 매개변수 이름을 나중에 바꿔도 호출하는 코드에 영향이 없도록 할 때, 키워드 전용 매개변수는 인수의 의미를 명확히 드러내야 할 때 사용합니다.
다중 반환
def calc(a, b):
return a + b, a - b, a * b, a / b
print(calc(3, 1))- 결과
- (4, 2, 3, 3.0)
반환값을 여러개로 지정할 경우 Tuple 형식으로 결과들이 반환됩니다.
엄밀히 말하면 함수는 여전히 값 하나를 반환하며, 쉼표로 나열한 값들이 하나의 Tuple로 묶여 반환되는 것입니다.
결괏값을 하나의 변수로 받는 res = calc(3, 1) 구조일 경우 res에 Tuple 전체가 할당됩니다.
만약, 결괏값을 함수 반환값 개수 만큼 사용하는 res1, res2, res3, res4 = calc(3, 1) 구조일 경우 각각의 결괏값이 변수에 할당됩니다.
def calc(a, b):
return a + b, a - b
res = calc(3, 1)
print(res, type(res))
plus, minus = calc(3, 1)
print(plus, minus)
try:
x, y, z = calc(3, 1)
except ValueError as e:
print(e)- 결과
- (4, 2) <class ‘tuple’>
4 2
not enough values to unpack (expected 3, got 2)
반환된 Tuple을 여러 변수로 나눠 받을 때는 변수의 개수와 반환값의 개수가 같아야 합니다.
변수가 3개인데 반환값이 2개라면 ValueError가 발생합니다. 필요 없는 값은 plus, _ = calc(3, 1)처럼 관례적으로 _ 변수로 받아 버립니다.
지역 변수와 전역 변수
함수 안에서 만든 변수는 함수 안에서만 사용할 수 있습니다. 이렇게 변수를 사용할 수 있는 영역을 범위(Scope)라 합니다.
함수 안에서 값을 할당한 변수를 지역 변수(Local variable), 함수 밖(파일의 최상위)에서 만든 변수를 전역 변수(Global variable)라 합니다.
범위가 나뉘어 있기 때문에 함수 안에서 x, result 같은 흔한 이름을 사용해도 함수 밖의 같은 이름 변수를 망가뜨리지 않습니다.
x = "global"
def local_only():
x = "local"
print("함수 안 :", x)
local_only()
print("함수 밖 :", x)
def read_global():
print("전역 읽기 :", x)
read_global()
def make():
y = 10
make()
try:
print(y)
except NameError as e:
print(e)- 결과
- 함수 안 : local
함수 밖 : global
전역 읽기 : global
name ‘y’ is not defined
local_only 함수 안의 x = "local"은 전역 변수 x를 바꾸는 것이 아니라, 함수 안에 같은 이름의 새 지역 변수를 만듭니다. 그러므로 함수가 끝난 뒤 출력한 전역 변수 x는 여전히 "global"입니다.
read_global 함수는 x에 값을 할당하지 않고 읽기만 하므로, 함수 밖의 전역 변수 x를 찾아 출력합니다.
make 함수의 지역 변수 y는 함수가 종료되면 사라지므로, 함수 밖에서 y를 사용하면 NameError가 발생합니다. 함수 안의 값을 밖에서 사용하려면 return으로 돌려줘야 합니다.
x = "global"
def outer():
x = "enclosing"
def inner():
print("inner :", x)
inner()
outer()
print(len)- 결과
- inner : enclosing
<built-in function len>
Python은 변수 이름을 만나면 다음 네 범위를 안쪽부터 바깥쪽 순서로 찾으며, 가장 먼저 찾은 값을 사용합니다. 각 범위의 앞 글자를 따서 LEGB 규칙이라 합니다.
| 순서 | 범위 | 설명 |
|---|---|---|
| 1 | L (Local) | 현재 함수 안 |
| 2 | E (Enclosing) | 현재 함수를 감싸고 있는 바깥 함수 안 |
| 3 | G (Global) | 파일(모듈)의 최상위 |
| 4 | B (Built-in) | print, len 등 내장 이름 |
inner 함수에는 지역 변수 x가 없으므로 한 단계 바깥인 outer의 x를 찾아 "enclosing"을 출력합니다. 전역 변수 x까지 찾아가지 않습니다.
len은 어디에서도 정의하지 않았지만 마지막 범위인 내장 범위에서 찾아집니다.
이 규칙 때문에 list = [1, 2]처럼 내장 이름과 같은 이름으로 변수를 만들면, 그 범위에서는 내장 함수 list()가 가려져 사용할 수 없게 됩니다.
count = 0
def increase():
count += 1
try:
increase()
except UnboundLocalError as e:
print(e)- 결과
- cannot access local variable ‘count’ where it is not associated with a value
함수 안에서 전역 변수를 읽는 것은 가능하지만, 값을 할당하면 그 이름은 함수 전체에서 지역 변수로 취급됩니다.
Python은 함수를 정의할 때 본문에 할당문(count += 1)이 있는지 보고 count를 지역 변수로 정합니다. 실행 시 count += 1은 count = count + 1이므로 오른쪽의 지역 변수 count를 먼저 읽어야 하는데, 아직 값이 없으므로 UnboundLocalError가 발생합니다.
count = 0
def increase_global():
global count
count += 1
def increase_return(n):
return n + 1
increase_global()
increase_global()
print(count)
count = increase_return(count)
print(count)- 결과
- 2
3
global 변수명을 선언하면 함수 안에서 전역 변수에 직접 할당할 수 있습니다. 두 번 호출했으므로 count는 2가 됩니다.
다만, 전역 변수를 함수 안에서 바꾸면 어떤 함수가 언제 값을 바꿨는지 추적하기 어려워집니다.
그러므로, increase_return처럼 값을 인수로 받고 결과를 반환한 뒤, 호출한 쪽에서 할당하는 방식을 권장합니다.
- Tip : 바깥 함수의 변수(Enclosing)에 할당하려면
global대신nonlocal 변수명을 사용합니다.
인수 전달 방식
함수에 인수를 전달하면 값이 복사되는 것이 아니라, 매개변수가 같은 객체를 가리키는 새 이름이 됩니다.
그러므로, 변경 가능한 객체를 전달하고 함수 안에서 그 객체를 수정하면 함수 밖에서도 변경 결과가 보입니다.
def add_item(L, item):
L.append(item)
def rebind(L):
L = [0]
data = [1, 2]
add_item(data, 3)
print(data)
rebind(data)
print(data)- 결과
- [1, 2, 3]
[1, 2, 3]
add_item 함수의 매개변수 L은 data와 같은 리스트를 가리키므로, L.append(3)은 data 자체에 원소를 추가합니다.
rebind 함수의 L = [0]은 리스트를 수정하는 것이 아니라 지역 변수 L이 새 리스트를 가리키도록 바꿀 뿐이므로, 함수 밖의 data에는 영향이 없습니다.
정수, 문자열, Tuple처럼 변경 불가능한 객체는 함수 안에서 수정할 방법이 없으므로, 함수 밖의 값이 바뀌지 않습니다.
- Tip : 전달받은 리스트를 바꾸지 않아야 한다면 함수 안에서
L = L.copy()처럼 복사본을 만들어 작업하거나, 새 리스트를 만들어 반환합니다.
일급 함수
def add(a, b):
return a + b
def func(x, y, z):
return x(y, z)
plus = add
print(plus(3, 4))
print(func(plus, 3, 4))- 결과
- 7
7
사용자 정의 함수를 다른 함수의 인수로 사용할 수 있으며, 변수나 자료구조에도 저장이 가능합니다.
사용자 정의 함수로 구성된 add 함수를 plus = add로 할당하여 add 함수를 plus로 사용이 가능합니다.
또한, func함수에서 x는 함수, y, z를 인수로 사용이 가능합니다.
func(함수, 인수1, 인수2)로 사용하여 사용자 정의 함수안에서 사용자 정의 함수를 호출 할 수 있습니다.
Python에서는 함수도 정수나 문자열처럼 하나의 객체로 취급되며, 이러한 함수를 일급 함수(First-class function)라 합니다.
- Tip :
plus = add는 함수를 호출하지 않고 함수 객체 자체를 할당합니다.plus = add(3, 4)처럼 괄호를 붙이면 함수의 반환값이 할당됩니다.
def add(a, b):
return a + b
def sub(a, b):
return a - b
ops = {"+": add, "-": sub}
print(ops["+"](10, 3))
print(ops["-"](10, 3))
print(add)
print(add.__name__)- 결과
- 13
7
<function add at 0x00000254F808B740>
add
함수를 사전의 값으로 저장하면, 연산 기호처럼 문자열로 어떤 함수를 실행할지 선택할 수 있습니다. ops["+"]는 add 함수 객체이고, 뒤에 (10, 3)을 붙여 호출합니다.
괄호 없이 add만 출력하면 함수 객체 자체가 출력되며, 메모리 주소는 실행할 때마다 달라집니다. __name__ 속성에는 함수를 정의할 때 사용한 이름이 저장되어 있습니다.
함수를 인수로 전달하는 방식은 이후 강좌의 lambda, filter, map, 정렬의 key 등에서 계속 사용됩니다.
정리
| 이름 | 설명 | 반환값/특징 |
|---|---|---|
| def 함수명(매개변수): | 함수를 정의 | 정의 시점에는 본문을 실행하지 않음 |
| return 값 | 값을 반환하고 함수를 즉시 종료 | 없으면 None 반환 |
| 위치 인수 | 순서대로 매개변수에 대응 | 키워드 인수보다 앞에 작성 |
| 키워드 인수 | 이름=값으로 매개변수에 대응 |
순서와 무관 |
| 매개변수=기본값 | 생략 시 사용할 값 | 정의할 때 한 번만 생성 |
| *args | 남은 위치 인수를 묶음 | Tuple |
| **kwargs | 남은 키워드 인수를 묶음 | Dict |
| / , * | 위치 전용 / 키워드 전용 구분 | /는 Python 3.8 이상 |
| global / nonlocal | 전역 / 바깥 함수 변수에 할당 | 남용하지 않고 반환값 사용 권장 |
- 인수는 위치 → 키워드 → 기본값 순서로 매개변수에 대응되며, 빠지거나 중복되면
TypeError가 발생합니다. - 변수는 L → E → G → B 순서로 찾으며, 함수 안에서 할당한 이름은 지역 변수가 됩니다.
- 인수는 객체 자체가 전달되므로, 변경 가능한 객체를 함수 안에서 수정하면 함수 밖에도 반영됩니다.
댓글 남기기