본문 바로가기

진리는어디에/Python

[Python] 유용하지만 잘 알려지지 않은 고급 트릭 5가지

본 포스트는 Charudatta ManwatkarHidden Gems of Python을 한글로 번역한 내용이다.

요즘 저에게는 재미로 파이썬 문서를 읽는 취미가 생겼습니다. 재미로 무엇인가를 읽을 때에 비로소 우리가 지금까지 놓치고 있었던 "오! 파이썬으로 이런 것도 할 수 있어요?"라고 할만한 흥미로운 것들을 찾는 경향이 있습니다.

1. Function 속성(attribute)

클래스 및 개체의 속성을 설정하는 방법과 유사하게 함수에도 속성을 설정할 수도 있습니다.

def func(x):
    intermediate_var = x**2 + x + 1

    if intermediate_var % 2:
        y = intermediate_var ** 3
    else:
        y = intermediate_var **3 + 1

    # setting attributes here
    func.optional_return = intermediate_var
    func.is_awesome = 'Yes, my function is awesome.'

    return y

y = func(3)
print('Final answer is', y)

# Accessing function attributes
print('Show calculations -->', func.optional_return)
print('Is my function awesome? -->', func.is_awesome)

우리는 10번째 줄에 'optional_return' 속성을 설정하고 11번째 줄에 'is_awesome' 속성을 설정했습니다. 우리는 19번과 20번 줄에서 외부에서 함수의 속성에 액세스했습니다. 코드의 출력은 다음과 같습니다:

Final answer is 2197
Show calculations --> 13
Is my function awesome? --> Yes, my function is awesome.

이것은 옵션이 일부 중간 변수를 사용하지만 함수가 호출될 때마다 return 문으로 명시적으로 반환하지 않으려는 경우에 유용하게 사용될 수 있습니다. 또한 함수의 속성(function attrubute)은 함수 내부, 외부 상관 없이 어느 곳에서든 설정할 수 있습니다.

함수 속성에 대한 보다 자세한 사항은 [여기]에 정리되어 있습니다.

2. For-else loop

파이썬에서는 for 루프에 else 절을 ​​추가할 수 있습니다. else 절은 실행 중에 루프 본문 내에서 break 문이 발견되지 않은 경우에만 트리거됩니다.

my_list = ['some', 'list', 'containing', 'five', 'elements']

min_len = 3

for element in my_list:
    if len(element) < min_len:
        print(f'Caught an element shorter than {min_len} letters')
        break
else:
    print(f'All elements at least {min_len} letters long')
All elements at least 3 letters long

else는 if 수준이 아니라 for 수준에서 들여쓰기됩니다. 여기서 길이가 3보다 짧은 요소는 없습니다. 따라서 break 문은 절대 만나지 않습니다. 따라서 else 절이 트리거되고(for 루프가 실행된 후) 위에 표시된 출력을 인쇄합니다.

break 문이 발생했는지 여부를 추적하는 별도의 변수를 사용하여 위와 동일하게 구현 될 수도 있습니다. 그리고 아마도 코드를 읽는 다음 사람에게 이 방법이 덜 혼란스러울 것입니다. 다음은 동일한 결과를 얻는 동등한 방법입니다 :

my_list = ['some', 'list', 'containing', 'five', 'elements']

min_len = 3

no_break = True
for element in my_list:
    if len(element) < min_len:
        print(f'Caught an element shorter than {min_len} letters')
        no_break = False
        break

if no_break:
    print(f'All elements at least {min_len} letters long')

그래도 알아두면 좋은 것 같아요. 보다 자세한 사항은 [여기]에 정리되어 있습니다.

3. ‘int’를 구분하기 편리하게 해주는 구분자

10000000과 100000000과 같은 정수를 한 눈에 구분하는 것은 종종 실수를 불러 옵니다.(둘이 다른 숫자인가요?). 만일숫자 사이에 쉼표를 사용하게 된다면 파이썬은 여러 정수의 튜플로 해석하기 때문에 영어에서 쉼표를 사용하는 것처럼 파이썬에서는 쉼표를 사용할 수 없습니다.

파이썬은 이것을 처리하는 매우 편리한 방법을 가지고 있습니다: 우리는 가독성을 향상시키기 위해 구분자로 밑줄을 사용할 수 있습니다. 따라서 1_000_000은 단일 int로 해석됩니다.

a = 3250
b = 67_543_423_778

print(type(a))
print(type(b))
print(type(a)==type(b))
<class 'int'>
<class 'int'>
True

4. eval() 과 exec()

파이썬은 문자열을 동적으로 읽고 파이썬 코드 처럼 처리하는 기능이 있습니다. 이것은 eval() 및 exec() 함수를 사용하여 달성됩니다('eval'은 표현식 평가용이고 'exec'는 명령문 실행용).

a = 3

b = eval('a + 2')
print('b =', b)

exec('c = a ** 2')
print('c is', c)
b = 5
c is 9

3행에서 eval() 함수는 입력 문자열을 파이썬 표현식으로 읽고, 평가하고, 결과를 변수 'b'에 할당합니다. 6행에서 exec() 함수는 입력 문자열을 파이썬 문으로 읽고 실행합니다.

동적으로 생성된 문자열을 전달할 수도 있습니다. 예를 들어, 코드에서 각각의 변수 선언을 수동으로 작성할 필요 없이 이름이 x_0, x_1, …, x_999인 1000개의 변수를 생성할 수 있습니다. 이것은 완전히 무의미한 기능처럼 보일 수 있지만 그렇지 않습니다.

파이썬 뿐만 아니라 일반적으로 프로그래밍의 더 넓은 맥락에서 eval/exec를 사용하면 동적으로 코드를 작성하여 컴파일 타임에도 표현할 수 없는 문제를 해결할 수 있습니다. exec는 말 그대로 파이썬 내부에 내장된 파이썬 인터프리터입니다. 따라서 특히 해결하기 어려운 문제가 있는 경우 해결할 수 있는 방법 중 하나는 *문제를 해결하는 프로그램을 작성*하는 프로그램을 작성한 다음 exec를 사용여 두 번째 프로그램을 실행하는 것입니다.

위 주제에 대해 'this beautiful explanation by Steven D’Aprano'에서 보다 자세히 읽으실 수 있습니다.

5. 줄임표(Ellipsis)

줄임표 또는 '...'는 None, True, False 등과 같은 내장 상수와 유사한 파이썬의 내장 상수입니다. 다음에 설명되는 내용들뿐만아니라 이에 국한되지 않는 다양한 방식으로 사용할 수 있습니다.

5.1 작성되지 않은 코드에 대한 플레이스 홀더(Placeholder)

pass와 유사하게 줄임표는 아직 코드가 완전히 작성되지 않은 경우 문법 오류를 통과하기 위해 자리 표시자로 사용할 수 있습니다. 

def some_function():
    ...
    
def another_function():
    pass

5.2 'None'의 대체

None은 빈 입력이나 반환을 표시하려는 경우 일반적인 선택입니다. 그러나 때때로 None은 함수의 예상 입력 또는 반환 중 하나가 될 수 있습니다. 이 경우 줄임표가 자리 표시자 역할을 할 수 있습니다.

# calculate nth odd number
def nth_odd(n):
    if isinstance(n, int):
        return 2 * n - 1
    else:
        return None


# calculate the original n of nth odd number
def original_num(m=...):
    if m is ...:
        print('This function needs some input')
    elif m is None:
        print('Non integer input provided to nth_odd() function')
    elif isinstance(m, int):
        if m % 2:
            print(f'{m} is {int((m + 1)/2)}th odd number')
        else:
            print(f'{m} is not an odd number')


original_num()

a = nth_odd(n='some string')
original_num(a)

b = nth_odd(5)
original_num(b)

original_num(16)

nth_odd() 함수는 n이 주어지면 n번째 홀수를 계산합니다. original_num() 함수는 n번째 홀수가 주어졌을 때 원래 숫자 n을 계산합니다. 여기서 None은 original_num() 함수에 대한 예상 입력 중 하나이므로 인수 m의 기본 자리 표시자로 사용할 수 없습니다. 코드의 출력은 다음과 같습니다.

This function needs some input
Non integer input provided to nth_odd() function
9 is 5th odd number
16 is not an odd number

5.3 NumPy 배열 슬라이싱

NumPy는 줄임표를 사용하여 배열을 슬라이스합니다. 다음 코드는 NumPy 배열을 슬라이싱하는 두 가지 동일한 방법을 보여줍니다 :

import numpy as np

a = np.arange(16).reshape(2,2,2,2)

print(a[..., 0].flatten())
print(a[:, :, :, 0].flatten())
[ 0 2 4 6 8 10 12 14]
[ 0 2 4 6 8 10 12 14]

따라서 '...'는 필요한 만큼 ':'가 있음을 나타냅니다.

줄임표의 부울 값

None(Boolean 값이 False인 경우)과 달리 줄임표(Ellipsis)의 Boolean 값은 True로 간주됩니다.

요약

요약하면 아래와 같습니다.

  1. 함수 어트리뷰트 : 객체처럼 함수에 속성을 할당합니다.
  2. For-else 루프: 루프가 break 문 없이 실행되었는지 추적하기 위해
  3. int 구분 기호: 32_534_478은 int입니다.
  4. eval() 및 exec(): 문자열을 파이썬 코드로 읽고 실행
  5. 줄임표: 다목적 내장 상수입니다.

부록 1. 같이 읽으면 좋은 글

유익한 글이었다면 공감(❤) 버튼 꾹!! 추가 문의 사항은 댓글로!!