[Python] zip함수와 파라미터 앞에 *
, **
는 어떤 의미인가?¶
zip함수는 동일한 개수로 이루어진 자료형을 묶어주는 역할을 한다.
동일한 위치에 있는 요소들을 가져와서 함께 묶어주는 것 같다.
나중에, 2차원 배열과 관련해서 열(=col)들을 가져올때 zip을 사용하면 유용할 것 같다.
[이유] 2차원 배열에서 각 행(=row)들은 인덱스를 통해서 가져오는 것이 쉽지만, 일반적인 방법으로 열(=col)을 가져오기 위해서는 for문이 필요하다.
- ex) 서로 다른 2개의 자료형을 묶어보자.
In [1]:
A = [1,2,3]
B = [4,5,6]
In [2]:
for node in zip(A, B):
print(node, type(node))
In [3]:
for i, j in zip(A, B):
print(i, j, type(i))
알고리즘 문제를 풀 때, 동서남북과 같이 이동방향에 따른 변화량을 고려해줘야 할때가 있는데
zip함수를 사용하면 깔끔하게 표현할 수 있을 것 같다.
- ex) 동서남북
In [4]:
dxs = (0, 0, 1, -1)
dys = (1, -1, 0, 0)
x, y = 0, 0 # x, y : 처음 위치
for dx, dy in zip(dxs, dys): # dx, dy : 변화량
nx, ny = x+dx, y+dy # nx, ny : 변한 후의 위치
print(nx, ny)
물론 굳이 zip을 이용할 필요는 없다.
좀 더 가독성이 좋아 보인다는 정도?
In [5]:
dxs = (0, 0, 1, -1)
dys = (1, -1, 0, 0)
x, y = 0, 0
for i in range(4):
nx, ny = x+dxs[i], y+dys[i]
print(nx, ny)
- ex) 서로 다른 3개 이상의 자료형을 묶어보자.
In [6]:
A = [1,2,3]
B = [4,5,6]
C = [7,8,9]
for node in zip(A, B, C):
print(node)
In [7]:
A = '1234'
B = '5678'
C = '4321'
D = '8765'
for node in zip(A, B, C, D):
print(node)
리스트 외에도 가능할까?
문자열끼리도 가능하고
문자열&리스트 처럼, 서로 다른 타입이어도 엮어주는 것이 가능하다.
집합도 가능하지만, 사용할 일이 없을 것 같다.
[이유] 내가 원하는 순서로 엮어진단 보장이 없다.
- ex) 다양한 자료형들을 엮어보자.
In [8]:
list(zip('abc', '123'))
Out[8]:
In [9]:
list(zip('abc', [1,2,3]))
Out[9]:
In [10]:
list(zip(set([1,2,3,4]), set([5,6,7,8])))
Out[10]:
만약 A와 B의 길이가 서로 다르다면 어떻게 될까?
길이가 더 짧은 자료형에 맞춰서 엮어준다.
매칭이 되지 않는 나머지 부분은 고려해주지 않는 것 같다.
- ex) 길이가 서로 다른 자료형을 엮어보자.
In [11]:
A = [1,2,3,4,5]
B = [6,7]
list(zip(A, B))
Out[11]:
In [12]:
for node in zip(A, B):
print(node)
In [13]:
for i, j in zip(A, B):
print(i, j)
2차원 리스트에서 행(=row)이 아닌 열(=col)을 순서대로 가져오고 싶다면 어떻게 해야 할까?
결과적으로, zip함수를 다양한 상황에 맞춰 응용하고 싶다면,
*args, *kwargs에 대한 개념을 알아둘 필요가 있다.
- ex) 2차원 리스트에서 열끼리 엮어보자.
In [14]:
alist = [[1,2,3], [4,5,6], [7,8,9]]
## 일단 2차원 리스트는 이렇게 생겼다.
for i in alist:
print(i)
zip함수의 인수로 2차원 리스트를 그냥 넣을 경우, 원하는 결과는 나오지 않는다.
In [15]:
list(zip(alist))
Out[15]:
In [16]:
for i in zip(alist):
print(i)
zip함수에 *args를 인수로 넣을 수 있다.
리스트를 그냥 입력하지 말고, *를 붙여서 입력하면 col끼리 서로 엮어준다.
In [17]:
for i in zip(*alist):
print(i)
- ex) map과 zip함수를 사용해서 2차원 리스트를 transpose해주는것이 가능하지 않을까?
In [18]:
alist = [[1,1,1], [2,2,2], [3,3,3]]
## 일단 2차원 리스트 alist는 이렇게 생겼다.
for i in alist:
print(i)
위의 2차원 리스트 (i, j)를 (j, i)로 간단하게 바꿔보자.
In [19]:
for i in map(list, zip(*alist)):
print(i)
새로운 리스트에 transpose한 것을 할당해주고 싶다면?
In [20]:
blist = list(map(list, zip(*alist)))
blist
Out[20]:
그렇다면, *args는 무엇인가?
*args :
- 파라미터를 몇개 받을지 모르는 경우에 사용한다.
- 튜플 형태로 전달된다.
- ex) 함수를 정의해서 알아보자
In [21]:
def func1(*args):
print(args, type(args))
print(*args, end='\n\n')
for i in args:
print(i)
In [22]:
## 변수 1개 입력받음
func1([1,2,3,4,5])
In [23]:
## 변수 여러개 입력 받는다면?
func1(1, 2, 3, 4)
In [24]:
func1('1', '2', '3', '4')
In [25]:
func1([1,2], [3,4,5], [6,7,8,9])
이 상태에서, 2차원 리스트를 인수값으로 넣어본다면?
In [26]:
func1([[1,2], [3,4]])
*args를 입력값으로 받는 함수에서 리스트를 그냥 입력하는 경우, 변수 1개 받은것으로 판단하는 것 같다.
리스트 앞에 *를 붙이면 어떻게 될까?
In [27]:
func1(*[[1,1], [2,2]])
In [28]:
func1(*[1,2,3,4,5])
신기한 점이 *args를 인수로 받는 함수에 리스트를 변수로 넣어주는 경우
리스트 앞에 *를 붙여서 보내주면 리스트 그 자체가 아니라,
그 안의 요소값들을 각각의 변수로 인식하는 것 같다.
- ex) *args를 인수로 받도록 설정하지 않은 함수여도 똑같이 적용될까?
In [29]:
def func2(xlist):
print(xlist)
for i in xlist:
print(i)
In [30]:
func2(*[1,2,3,4,5])
역시 안된다.
*args의 의미를 다시 생각해보면, 변수를 몇개 받을지 알지 못하는 특수한 상황에 쓰인다는 점.
이를 주의해서 사용하면 될 것 같다.
*args외에 특정한 다른 변수도 인수값으로 받고 싶다면 어떻게 해야 할까?
결과적으로 말하자면,
함수를 정의할 때, 입력받을 인수(=변수)의 순서를 조정해줘야 한다.
- ex) 특정 변수 n과 임의 몇개를 받을 지 모르는 *args변수를 인수로 설정해보자.
만약 *args가 n보다 앞에 온다면?
In [31]:
def func3(*args, n):
print(n)
for i in args:
print(i)
In [32]:
func3(1,2,3,4,5,6)
변수 n을 입력받지 못했다는 TypeError가 발생하고 있다.
이번엔 변수 n을 먼저 입력받도록 함수를 정의해보자.
In [33]:
def func4(n, *args):
print(n)
for i in args:
print(i)
In [34]:
func4(100, 1,2,3,4,5)
n = 100
*args = 1, 2, 3, 4, 5 라고 인식한 것 같다.
특정 변수들을 먼저 할당해주고, 나머지들을 임의 변수로 몰아주는 방식을 사용해야 하는 것 같다.
즉, 특정 변수를 *args보다 먼저 정의해줘야 한다.
**kwargs는 무엇인가?
**kwargs :
- 키워드를 함께 보낼 수 있다.
- 딕셔너리 형태로 전달된다.
- ex) 간단히 함수로 살펴보자.
In [35]:
def func5(**kwargs):
print(kwargs)
print()
print(kwargs.keys())
print()
print(kwargs.values())
print()
for i, j in kwargs.items():
print(i, j)
In [36]:
func5(name='juhee', sex='female', age=27)
주의할 점은, 문자열 키워드를 따로 str 변환해줄 필요가 없다는 점?
그리고 맨 앞 자리가 숫자형태를 띄고 있으면 SyntaxError가 일어나는 것 같다.
In [37]:
func5('1'=100, '2'=200)
In [38]:
func5(1=100, 2=200)
In [39]:
func5(1x=100, 2x=200)
숫자를 앞에만 두지 않으면 되는 것 같다.
In [40]:
func5(x1 = 100, x2 = 200)
*args와 **kwargs를 동시에 사용하면 어떻게 될까?
참고로, args와 kwargs는 arguments, keyword arguments 에서 따온 것이다.
In [41]:
def func6(*args, **kwargs):
print(args)
print(kwargs)
In [42]:
func6(1,2,3,4,5, name = 'juhee', age = 27)
특정 변수와 *args가 순서에 영향을 받았던 것 처럼,
*args와 **kwargs도 서로 영향을 받을까?
둘의 순서를 바꿔보자.
In [43]:
def func6(**kwargs, *args):
print(args)
print(kwargs)
정의조차 불가능하다.
즉, 특정변수와 *args, **kwargs를 인수로 함께 정의하고 싶다면
순서를 주의해야 한다.
'Python > 내장함수&기타' 카테고리의 다른 글
[Python/내장함수] 클래스(=Class) 기본 사용 방법 (0) | 2020.05.15 |
---|---|
[Python/내장함수] lambda, map, filter, reduce 함수 사용법 (0) | 2020.04.19 |
[Python/기타] 알고리즘(set을 이용한) 시간초과에 도움이 되는 팁 (0) | 2020.03.14 |
[Python/기타] 파이썬 시간 복잡도(Time Complexity) (0) | 2020.02.26 |
[Python/내장함수] 예외 처리하기 (try / except문) (0) | 2020.01.03 |