C언어에서 pointer 연산자인 *
(asterisk)는 Python에서 상당히 낯설게 동작한다.
(특히, PEP3132, PEP448 등에서 그 기능이 무지 많아져서... --;;)
double asterisk **
와 함께 packing과 unpacking 기능으로 정말 많이 사용되기 때문에 한번은 정리를 해보는게 도움이 된다.
크게 function과 관련되어서는 두가지 mode로 동작한다.
- function을 define 할 때 parameter에서 사용되는 경우 (= function header에서 사용되는 경우) : packing
- function call에서 argument로 사용되는 경우 : unpacking
이와 유사하지만 조금 색다르게 보이는 경우도 다음과 같이 있다.
- assignment의 left side에 놓이는 경우 : packing
list
를 나타내는 square brackets[ ]
안에 놓이는 경우 : unpackinglist
외에set
{ }
이나tuple
( )
안에 놓이는 경우 : unpacking
Function's arguments and paraemters
다음 예제는 function header에서 parameter에 사용되는 경우로 packing을 수행하는 예이다.
def test_func(*pack):
print(type(pack),pack)
for idx, c in enumerate(pack):
print(f'{idx:02} : {str(c):>14}')
test_func('사과','바나나','복숭아','자두')
test_func
에 주어진 복수의 argument들이pack
이라는 이름을 가지는 tuple 객체로 packing 됨을 확인할 수 있음.- positional arguments의 순서를 유지하는 immutable sequence type 인 tuple로 묶임.
- 위와 같은 방법으로 사용되는 경우 관례적으로
*args
라고 parameter를 기재하나, 위 예제에서는 packing을 강조하기 위해 이름을 바꿈. (가급적 관례를 따르기를 권함)
결과는 다음과 같음.
<class 'tuple'> ('사과', '바나나', '복숭아', '자두')
00 : 사과
01 : 바나나
02 : 복숭아
03 : 자두
다음은 *
를 function call에서의 argument로 사용할 경우의 예임.
unpack = ['사과', '바나나', '복숭아', '자두']
test_func(*unpack) # test_func('사과','바나나','복숭아','자두') 와 동일.
*unpack
으로 argument를 넘겨주면,unpack
list를 unpacking하여 각각의 item들이 arguments로 주어지게 됨.- 때문에 앞서 결과와 같은 arguments를 넘겨준 것과 같은 결과를 보임.
만약 다음과 같이 *
를 빼고 unpack
만을 argument로 넘겨주는 것을 실행해보자.
>>> test_func(unpack)
<class 'tuple'> (['사과', '바나나', '복숭아', '자두'],)
00 : ['사과', '바나나', '복숭아', '자두']
- list
unpack
이 통째로 하나의 argument로 주어진다 (unpacking이 일어나지 않음) - 때문에 하나의 argument로 처리되고 list
unpack
에서의__str__
special method의 반환값에 해당하는 문자열이 출력됨.
다른 경우들.
다음은 assignment의 left side에 놓이는 경우에 대한 예이다.
first, second, *remaining = unpack
print(first)
print(second)
print(remaining)
print(type(remaining))
a,b,c=1,2,3
과 같은 다중할당은 left side와 right side의 item의 수가 같아야함.- 하지만, 위와 같이
*remaining
이 left side에서 사용되면, 나머지 item들을 모두 packing한 listr객체remaining
이 됨. - 이는 마치 function header에서
*
로 시작하는 parameter에 여러 arguments가 packing되는 것과 개념적으로 유사함.
결과는 다음과 같음.
사과
바나나
['복숭아', '자두']
<class 'list'>
list
를 나타내는 square brackets [ ]
안에 놓는 경우에 대한 예는 다음과 같다.
>>> new_list = [ *unpack, *reversed(unpack)]
>>> new_list
['사과', '바나나', '복숭아', '자두', '자두', '복숭아', '바나나', '사과']
- 위의 code snippet은
unpack
list를 역순으로 뒤에 concatenate 시킴.
*
을 이용한 unpacking을 활용한 예로서 PEP 448에서 추가된 기능이다.
이전에 위와 같은 처리는 다음과 같이 해야 했음.
new_list = [list(unpack), list(reversed(unpack))]
- 같은 결과를 얻을 수 있지만, 좀 더 비 효율적으로 알려져 있음.
아래와 같이 timeit
을 통해 확인 가능함.
import timeit
unpack = ['사과', '바나나', '복숭아', '자두']
def test_fn0 ():
ret = [ *unpack, *reversed(unpack)]
return ret
print('the case of * :', round(timeit.timeit(test_fn0, number= 1000000),4))
def test_fn1 ():
ret = [list(unpack), list(reversed(unpack))]
return ret
print('the case of old approach :', round(timeit.timeit(test_fn1, number= 1000000),4))
이를 많이 사용하는 경우는
다음과 같이 맨 앞의 item을 맨 뒤로 보내고, 나머지 item들을 하나씩 앞으로 보내는 처리 등을 할 때 매우 유용하다.
>>> new_list = [*unpack[1:],unpack[0]]
>>> new_list
['바나나', '복숭아', '자두', '사과']
또는 두 list의 union을 구할 때에도 다음과 같이 활용가능하다.
u = {*a, *b} # set().union(a,b)
Keyword-only arguments 처리하기.
function header에서 다음과 같이 *
를 parameter로 사용할 경우,
특정 parameter 이후로는 positional arguments를 사용하지 못하도록 막을 수 있다.
def test_fn( a, *, key0=None):
""" a 파라메터까지만 positional argument로 할당가능하고,
* 이후에 있는 key0는 keyword argument로만 사용가능함.
"""
print(f'a={a}')
print(f'key0={key0}')
test_fn(a)
test_fn(a,1)
*
이후로는 무조건 keyword argument로만 사용가능해짐.
https://gist.github.com/dsaint31x/d139f4a7dac95a301bc6a06f3bd12755
'Python' 카테고리의 다른 글
[Python] Decorator (0) | 2023.08.18 |
---|---|
[Python] PEP 8 : Style Guide for Python Code (0) | 2023.08.04 |
[Python] while statement, break and continue (0) | 2023.07.28 |
[Python] if, elif, else statements (0) | 2023.07.28 |
[Python] Boolean Operators, Relational Operators, and Membership Operator (0) | 2023.07.28 |