입력 데이터를 처리하는 방식을 이해하는 것은 함수 정의문의 매개변수와 함수 호출의 인자간의 매핑을 이해하는 것과 같다. 파이썬은 이러한 매핑 관계 4가지를 제공한다.
고정 위치 인자와 가변 위치 인자 처리
고정 위치인자 처리
함수를 작성할 때 매개변수에 x, y, z를 이름으로 정의하면 함수를 호출할 때도 3개의 인자를 전달해야 한다. 이런 방식을 고정 위치인자라고 하고, 함수를 호출할 때도 고정으로 인자를 전달해 위치에 맞게 1대1로 매핑해서 처리한다.
함수의 내부 이름공간에 어떻게 매개변수와 인자가 매칭되는지 내장함수 locals를 실행해 알아보자.
def func(x, y, z):
print(f"내부 이름공간 : {locals()}")
return x + y + z
func(1, 2, 3)
[결과]
내부 이름공간 : {'x': 1, 'y': 2, 'z': 3}
6
고정 매개변수를 정의할 때 매개변수에 초깃값(defaults)을 지정할 수 있다. 이 기능은 함수를 호출할 때 인자를 주지 않으면 초깃값이 매개변수에 할당되기 때문에 유용하다.
def func(x=1, y=2, z=3):
print(f"내부 이름공간 : {locals()}")
return x + y + z
func()
[결과]
내부 이름공간 : {'x': 1, 'y': 2, 'z': 3}
6
참고로 인자를 적게 주면 첫 번째 인자부터 할당된다. 위 함수에서는 인자를 1개만 주면 x, 2개를 주면 x와 y에 할당된다.
가변 위치인자 처리
함수를 호출할 때 매개변수보다 많은 인자를 전달할 수 있다. 이때 사용하는 방식이 가변 위치인자이다.
이 방식을 알아보기 전에 여러 값을 변수에 할당하는 방식부터 알아보자.
2개의 변수에 6개의 원소를 가진 리스트를 할당하면 오류가 발생한다. 1대1 매핑이 되지 않기 때문이다. 이를 방지하기 위해 2개의 변수 중 하나에 *를 붙여 여러 원소를 받을 수 있다는 표시를 해야 한다. 이를 언패킹이라 한다.
두 변수 x, y중 y 앞에 *을 붙여 여러 개의 원소를 받을 수 있도록 하자. 이러면 1대1 매핑을 한 후 남은 모든 원소를 y에 리스트로 넣는다.
x, *y = [1, 2, 3, 4, 5, 6]
x, y
[결과]
(1, [2, 3, 4, 5, 6])
이제 함수로 들어가보자. 매개변수 args로 정의하자. 가변 인자를 받는 매개변수의 이름은 args로 작성하는 것이 관례다.
가변 인자는 매개변수에 튜플로 할당된다.
def func(*args):
print(f"내부 이름공간 : {locals()}")
return args
func(1, 2, 3, 4, 5)
[결과]
내부 이름공간 : {'args': (1, 2, 3, 4, 5)}
(1, 2, 3, 4, 5)
가변 키워드 인자 처리?
딕셔너리는 키와 값의 한 쌍을 원소로 구성하는 자료구조다. 딕셔너리를 하나 정의해서 내부의 원소를 키와 값으로 검색하는 items 메소드를 사용해보자. 순환문에 딕셔너리의 items 메소드를 실행하면 키와 값을 분리해서 가져온다.
함수를 정의할 때 하나의 매개변수를 정의한다. 이 매개변수는 가변 키워드 인자를 받으므로 별을 2개 붙인다.
관행적으로 키워드 인자를 받는 매개변수의 이름으로는 kwargs를 사용한다.
def func(**kwargs):
print(f"내부 이름공간 : {locals()}")
res = 0
for k, v in kwargs.items():
res += v
return res
func(x=1, y=2, z=3, a=4)
[결과]
내부 이름공간 : {'kwargs': {'x': 1, 'y': 2, 'z': 3, 'a': 4}}
10
고정 매개변수, 가변 매개변수 혼용하기
매개변수를 정의할 때 순서는 항상 고정 위치 매개변수를 맨 처음 작성하고 그 다음에 가변 위치 매개변수를 사용할 수 있다.
전달된 인자를 모두 합산하려면 고정 위치 매개변수는 직접 더하지만 가변 위치 매개변수는 내부의 원소를 하나씩 반복해서 더해야 한다.
def func(x, y, z, *args):
print(f"내부 이름공간 : {locals()}")
res = x + y + z
for i in args:
res += i
return res
func(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
[결과]
내부 이름공간 : {'x': 1, 'y': 2, 'z': 3, 'args': (4, 5, 6, 7, 8, 9, 10)}
55
이번에는 위치 매개변수 다음 2개의 고정 키워드 매개변수에 초깃값을 지정해 정의하자. 가변 위치 매개변수 다음에는 고정 키워드 매개변수가 올 수 있다. 항상 순서를 주의하자!
아래 함수를 호출할 때 고정 위치 인자만 전달했지만 실행된다. 가변위치 인자는 인자가 없어도 된다! 아무것도 없을 때는 빈 튜플로 저장된다.
def func(x, y, z, *args, a=1, b=2):
print(f"내부 이름공간 : {locals()}")
res = x + y + z + a + b
for i in args:
res += i
return res
func(1, 2, 3)
[결과]
내부 이름공간 : {'x': 1, 'y': 2, 'z': 3, 'a': 1, 'b': 2, 'args': ()}
9
고정 위치인자와 고정 키워드 인자만 전달받는 함수를 정의하려면 가변 위치 매개변수 자리에 별표만 붙인다. 가변 위치 매개변수에는 이름이 없어 실제 가변 위치인자를 받을 수 없다!
def func(x, y, z, *, a=10, b=20):
print(f"내부 이름공간 : {locals()}")
res = x + y + z + a + b
return res
func(1, 2, 3, a=1, b=2)
[결과]
내부 이름공간 : {'x': 1, 'y': 2, 'z': 3, 'a': 1, 'b': 2}
9
인자 5개를 전달하면 예외가 발생한다. 이유는 3개의 고정 위치인자는 매핑되지만 나머지 2개의 인자가 매핑되지 않기 때문이다. 단순히 별표만 사용하면 가변 위치 인자를 받을 수 없다.
def func(x, y, z, *, a=10, b=20):
print(f"내부 이름공간 : {locals()}")
res = x + y + z + a + b
return res
func(1, 2, 3, 4, 5)
[결과]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
c:\Users\reo91\OneDrive\Coding Practice\P C++, Python\Python3.ipynb 셀 20 in <cell line: 7>()
3 res = x + y + z + a + b
5 return res
----> 7 func(1, 2, 3, 4, 5)
TypeError: func() takes 3 positional arguments but 5 were given
매개변수의 순서는 고정 위치, 가변 위치, 고정 키워드, 가변 키워드 순이라는 것을 기억하자!
모든 매개변수가 가능한 구조!
함수를 정의할 때 다양한 인자를 전달해서 함수를 호출할 때가 있다. 이때 가변 위치 매개변수와 가변 키워드 매개변수 순으로 정의를 하면 다양한 인자를 받을 수 있는 함수를 정의할 수 있다.
def func(*args, **kwargs):
print(f"내부 이름공간 : {locals()}")
res = 0
for i in args:
res += i
for k, v in kwargs.items():
res += v
return res
func(1, 2, 3, 4, 5, a=10, b=20)
[결과]
내부 이름공간 : {'args': (1, 2, 3, 4, 5), 'kwargs': {'a': 10, 'b': 20}}
45
색인 검색 함수를 메소드로 전환하기
이전 강의에서 여러 개의 인덱스를 인자로 전달해 색인 검색을 하는 함수를 정의했다.
이번에는 클래스의 이름으로 해당 클래스의 메소드를 이용하거나, 객체를 선언해 메소드를 이용하는 방법을 알아보자.
l = [1, 2, 3, 4, 5, 6]
class Index: # 클래스를 정의할 때 상속을 정의하지 않으면 자동으로 object 클래스 상속
@staticmethod # 데코레이터로 함수를 정적 메소드로 변환 (이후에 다룹니다!)
def index(iter, *args): # 정적 메소드는 클래스에서 함수를 그대로 사용하도록 정의
res = []
for i in args:
if i < len(iter):
res.append(iter[i])
else:
continue
return res
print(Index.index(l, 1, 2, 5))
[결과]
[2, 3, 6]
함수를 클래스의 메소드로 구성하려면 함수의 첫 번째 인자를 객체를 만들 때 속성으로 할당해야 한다. 그리고 함수를 메소드로 변환하려면 첫 번째 인자에 self를 넣고 내부를 개체의 속성으로 처리하게 로직을 수정한다.
l = [1, 2, 3, 4, 5, 6]
class Index: # 클래스를 정의할 때 상속을 정의하지 않으면 자동으로 object 클래스 상속
def __init__(self, iter): # 객체가 생길 때 __init__함수에 self, iter을 인자로 넣음
self.iter = iter # 객체가 만들어질 때 iter 속성에 iter을 할당
def index(self, *args): # 인스턴트 메소드도 함수로 정의한다.
res = []
for i in args:
if i < len(self.iter):
res.append(self.iter[i])
else:
continue
return res
var = Index(l)
print(var.index(1, 2, 5))
[결과]
[2, 3, 6]
참고
- 한권으로 개발자가 원하던 파이썬 심화 A to Z, 문용준/문성혁 저
'◎ Python > 파이썬 심화 (책)' 카테고리의 다른 글
[파이썬 심화] 14. 클래스와 객체 구조를 알아보자 (0) | 2022.09.07 |
---|---|
[파이썬 심화] 13. 클래스 정의하기 (1) | 2022.09.06 |
[파이썬 심화] 11. 함수 정의하기 (0) | 2022.09.02 |
[파이썬 심화] 10. 여러 조건에 따라 기능 선택하기 (0) | 2022.09.01 |
[파이썬 심화] 9. 여러 문장을 묶어 반복 실행하기 (0) | 2022.08.31 |
자기계발 블로그