[Python] 파이썬스럽게 코드 쓰기, Pythonic Code 내용 총정리

2024. 5. 4. 22:06Algorithm

1. Overview, 파이썬 일반 코드와 파이썬스러운(Pythonic) 코드를 비교해보자!


개발자라면, 빠질 수 없이 공부를 해야 되는 부분이 클린 코드와 관련된 점이다.
이 클린 코드 관점에서 파이썬이라는 프로그래밍 언어는 굉장히 독특한 점을 갖고 있는데, 그것은 파이썬스러움이라는 단어를 보면 알 수 있다.

파이썬스러운(Pythonic)이라 불리는 파이썬의 클린코드는 파이썬의 기능을 최대한 활용해서 코드를 짜는 것을 의미한다.

사실 좋은 코드란 개념이 명확하게 딱 이거다 정해져 있는 것은 아니다.
코드의 본질은 프로그램이 잘 동작하는 것이기 때문에.
하지만, 여기에 본인을 포함한 다른 개발자가 읽기 쉽고, 유지보수까지 쉽다면, 가장 좋지 않겠는가.

그래서 코드의 가독성과 일관성을 위해 우리는 파이썬스럽게(Pythonic) 코딩을 할 필요가 있다.
게다가 이 파이썬스러운 코드는 이미, 나보다 훌륭한 파이썬 프로그래머들이 정해놓은 스타일이니까... 이걸 따라서 나쁠 건 없지 않을 것이므로.

우선 가장 기초적인 for문을 통해 일반 파이썬 코드와, 파이썬스러운 코드를 직관적으로 비교해보자.

  • colors에는 여러 색을 String형태로 담아준다.
  • 빈 문자열 result를 생성해주고, colors를 반복시키면서 result에 계속 문자열을 더해준다.
colors = ["red", "blue", "green", "yellow"]

# 일반 파이썬 코드
normal_result = ""
for s in colors:
    normal_result += s
print(normal_result)


# 파이썬스러운 코드
pythonic_result = "".join(colors)
print(pythonic_result)

두 개 다 같은 결과물(redbluegreenyellow)을 도출하는 코드인데,
언뜻 봐도 파이썬스러운 코드가 훨씬 간결하고, 직관적이라는 것을 알 수 있다. (그리고 뭔가 있어 보이지 않는가 ^___^)

 

2. Split & Join : String과 List를 자유롭게 변환시키기


Split과 Join을 사용하여 String 타입과 List 타입을 자유롭게 변환시킬 수 있는 방법을 알아보도록 하겠다.

이 부분은 가장 파이썬스러움을 잘 나타내 주는 부분이자, 앞으로 굉장히 자주 사용하게 되는 부분이니 잘 익혀두도록 하자.

2-1) Split 함수: 나누는 역할

split 함수는 String 타입의 값을 어떠한 기준으로 나누어, List 타입의 값으로 변환해주는 역할을 한다.

기본 사용방법은 문자열.split(나누는 기준)으로 사용한다. 보다 자세한 사용법은 아래 코드를 통해 살펴보도록 하자.

# 1. 빈칸을 기준으로 문자열을 나누기
items = 'zero one two three'.split()
print(items)	# ['zero', 'one', 'two', 'three']

# 2. 콤마(,)를 기준으로 문자열을 나누기
items = 'zero,one,two,three'.split(',')
print(items)	# ['zero', 'one', 'two', 'three']

# 3. 콤마(,)를 기준으로 문자열을 나눈 후, 각 a b c d 변수로 unpacking하기
items = 'zero,one,two,three'
a, b, c, d = items.split(",")
print(a)		# zero
print(b)		# one
print(c)		# two
print(d)		# three

 

2-2) Join 함수: 합치는 역할

Join 함수는 String 타입의 값을 담고 있는 List를 합쳐, 하나의 String 타입의 값으로 변환해주는 역할을 한다.

기본 사용방법은 연결방법.join(String 타입의 값을 담고 있는 List)으로 사용한다.
역시 자세한 내용은 아래 코드를 통해 살펴보도록 하자.

# 1. join 함수 기본
colors = ['red', 'blue', 'green', 'yellow']
result = ''.join(colors)
print(result)		# redbluegreenyellow

# 2. 한 칸씩 띄우는 연결방법을 사용해서 join 함수 사용 
result = ' '.join(colors) 
print(result)		# red blue green yellow

# 3. 콤마(, ) 연결방법을 사용해서 join 함수 사용 
result = ', '.join(colors) # 연결 시 ", "으로 연결
print(result)		# red, blue, green, yellow

 

3. List Comprehension : 파이썬에서 리스트를 쉽고 빠르게 만드는 방법


이번에는 List Comprehension에 대해서 배워보도록 하자. 진짜 내가 애용하는 기능이다.

List Comprehension을 직관적으로 해석해보면, 포괄적인 List, 포함되는 리스트라는 의미이다. 아직 무슨 말인지 감이 안 올 거다.
그냥 간단하게 말하면, 리스트를 만들 때 쉽고 빠르게 만들 수 있는 방법이 바로 List Comprehension이다.

실제로, for문 while문보다 속도가 빠르다고 하니, 기존 리스트를 만드는 방법과 어떤 차이점을 갖고 있는지 알아보자.

 

3-1) 파이썬 일반 코드와 파이썬스러운 코드의 비교 -> 기본

result = []

for i in range(10):
    result.append(i)

print(result)		# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
result = [i for i in range(10)]    # List Comprehension 사용
print(result)		# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

 

3-2) 파이썬 일반 코드와 파이썬스러운 코드의 비교 -> Nested For Loop

word_1 = "Hello"
word_2 = "World"
result = []

for i in word_1:
    for j in word_2:
        result.append(i+j)
        
print(result)	# ['HW', 'Ho', 'Hr', 'Hl', 'Hd', 'eW', 'eo', 'er', 'el', 'ed', 'lW', 'lo', 'lr', 'll', 'ld', 'lW', 'lo', 'lr', 'll', 'ld', 'oW', 'oo', 'or', 'ol', 'od']
word_1 = "Hello"
word_2 = "World"
result = [i+j for i in word_1 for j in word_2]

print(result)	# ['HW', 'Ho', 'Hr', 'Hl', 'Hd', 'eW', 'eo', 'er', 'el', 'ed', 'lW', 'lo', 'lr', 'll', 'ld', 'lW', 'lo', 'lr', 'll', 'ld', 'oW', 'oo', 'or', 'ol', 'od']

 

3-3) 파이썬 일반 코드와 파이썬스러운 코드의 비교 -> 조건 추가

case_1 = ["A", "B", "C"]
case_2 = ["D", "E", "A"]
result = []

for i in case_1:
    for j in case_2:
        if not(i==j):
            result.append(i+j)

result.sort()
print(result)	# ['AD', 'AE', 'BA', 'BD', 'BE', 'CA', 'CD', 'CE']
case_1 = ["A", "B", "C"]
case_2 = ["D", "E", "A"]

result = [i+j for i in case_1 for j in case_2 if not(i==j)]
result.sort()

print(result)	# ['AD', 'AE', 'BA', 'BD', 'BE', 'CA', 'CD', 'CE']

 

3-4) 파이썬 일반 코드와 파이썬스러운 코드의 비교 -> split 함수와 응용

words = 'The quick brown fox jumps over the lazy dog'.split()
print(words)

stuff = [[w.upper(), w.lower(), len(w)] for w in words]

for i in stuff:
    print(i)
    
"""
['The', 'quick', 'brown', 'fox', 'jumps', 'over', 'the', 'lazy', 'dog']
['THE', 'the', 3]
['QUICK', 'quick', 5]
['BROWN', 'brown', 5]
['FOX', 'fox', 3]
['JUMPS', 'jumps', 5]
['OVER', 'over', 4]
['THE', 'the', 3]
['LAZY', 'lazy', 4]
['DOG', 'dog', 3]
"""

 

4. Enumerate & Zip : 리스트의 값을 효율적으로 추출하는 방법


이번에는 리스트의 값을 추출할 때 함께 인덱스를 추출할 수 있는 방법으로 이용되는 enumerate와,
두 개 이상의 list 값을 병렬적으로 추출할 수 있는 zip 모듈을 사용하여 pythonic code를 작성하는 방법을 알아보자.

 

4-1) Enumerate: 리스트의 값과 인덱스를 함께 추출하는 방법

{i:j for i,j in enumerate('Gachon University is an academic institute located in South Korea.'.split())}
"""
{0: 'Gachon',
 1: 'University',
 2: 'is',
 3: 'an',
 4: 'academic',
 5: 'institute',
 6: 'located',
 7: 'in',
 8: 'South',
 9: 'Korea.'}
 """

 

4-2) Zip: 두 개 이상의 List 값을 병렬적으로 추출하는 방법

# 1. 기본 for문을 사용해서, zip 함수를 사용할 경우

alist = ['a1', 'a2', 'a3']
blist = ['b1', 'b2', 'b3']

for a, b in zip(alist, blist):
    print(a, b)
    
"""
a1 b1
a2 b2
a3 b3
"""
# 2. List Comprehension을 사용해서, zip 함수를 사용할 경우

a, b, c = zip((1, 2, 3), (10, 20, 30), (100, 200, 300))
print(a, b, c)

print([sum(x) for x in zip((1, 2, 3), (10, 20, 30), (100, 200, 300))])

"""
(1, 10, 100) (2, 20, 200) (3, 30, 300)
[111, 222, 333]
"""
# 3. enumerate와 zip 함수를 함께 사용할 경우

alist = ['a1', 'a2', 'a3']
blist = ['b1', 'b2', 'b3']

for i, (a, b) in enumerate(zip(alist, blist)):
    print(i, a, b)
    
"""
0 a1 b1
1 a2 b2
2 a3 b3
"""

 

5. Asterisk


Asterisk(*) 연산자는 곱셈뿐만 아니라, 파이썬스러운 코드를 다루고자 할 때 다양한 사용도를 가지게 된다.

어떤 사용도가 있는지 아래에서 확인해보자.

  • 곱셈 및 거듭제곱 연산으로 사용할 때
  • 리스트형 컨테이너 타입의 데이터를 반복 확장하고자 할 때
  • 가변인자 (Variadic Arguments)를 사용하고자 할 때
  • 컨테이너 타입의 데이터를 Unpacking 할 때

오늘 여기서는 세 번째 가변인자를 사용하는 방법과, 컨테이너 타입의 데이터를 Unpacking하는 방법을 간략하게 살펴보려고 한다.

 

5-1) 가변인자 (Variadic Arguments)를 사용하고자 할 때

우리는 종종 함수에서 가변인자를 필요로 할 때가 있다.
여기서 말하는 가변인자란, 들어오는 인자의 개수를 모른다거나, 그 어떤 인자라도 모두 받아서 처리를 해야 하는 때를 의미한다.

파이썬에서는 인자의 종류가 2가지가 있다.
하나는 그대로 위치에 따라 정해지는 인자인, positional arguments이고, 하나는 키워드를 가진 이름을 가진 인자 keyword arguments이다.

def asterisk_test(a, *args):
    print(a, args)
    print(type(args))

asterisk_test(1,2,3,4,5,6)	# 1 (2, 3, 4, 5, 6) <class 'tuple'>
def asterisk_test(a, **kargs):
    print(a, kargs)
    print(type(kargs))

asterisk_test(1, b=2, c=3, d=4, e=5, f=6)	# 1 {'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6} <class 'dict'>

 

5-2) 컨테이너 타입의 데이터를 Unpacking 할 때

def asterisk_test(a, *args):
    print(a, args)
    print(type(args))
    
asterisk_test(1, *(2,3,4,5,6))	# 1 (2, 3, 4, 5, 6) <class 'tuple'>
def asterisk_test(a, args):
    print(a, *args)
    print(type(args))
    
asterisk_test(1, (2,3,4,5,6))	# 1 2 3 4 5 6 <class 'tuple'>
a, b, c = ([1, 2], [3, 4], [5, 6])
print(a, b, c)

data = ([1, 2], [3, 4], [5, 6])
print(*data)

"""
[1, 2] [3, 4] [5, 6]
[1, 2] [3, 4] [5, 6]
"""
def asterisk_test(a, b, c, d,):
    print(a, b, c, d)
    
data = {"b":1, "c":2, "d":3}
asterisk_test(10, **data)	# 10 1 2 3
for data in zip(*([1, 2], [3, 4], [5, 6])):
    print(data)
    
"""
(1, 3, 5)
(2, 4, 6)
"""