규도자 개발 블로그

Elixir의 Pipe연산자(|>)를 파이썬에서쓰기 위한 발악 본문

Python/Python

Elixir의 Pipe연산자(|>)를 파이썬에서쓰기 위한 발악

규도자 (gyudoza) 2022. 8. 28. 12:52

Elixir의 Pipe연산자(|>)를 파이썬에서쓰기 위한 발악

Airflow를 사용하다가 이런 생각을 했다. Airflow의 DAG는 원래는 비트연산자인 >>(rshift)를 이용해서 로 Task의존성을 명시해서 그것을 시각화하여 보여주곤 하는데 이걸 이용하면 Elixir에서처럼 Pipe연산자를 만들 수 있지 않을까..? 바로 작업에 착수했다. 일단은

class Add:
    def __init__(self, num: int):
        self.num = num

    def __rshift__(self, other: 'Add'):
        return Add(self.num + other.num)

    def __str__(self):
        return str(self.num)

이런 함수를 만들어서

num1 = Add(1)
num2 = Add(2)
num3 = Add(90)

print(num1 >> num2 >> num3)
# 93

이렇게 쓰면 뭔가 어설프게 흉내는 낼 수 있는데 형태를 보면 알 수 있다시피 이건 +연산을 하는 경우에만 rshift chain으로 묶어서 사용할 수 있는 형태이므로 사실 실용성은 0에 수렴한다고 할 수 있겠다.

 

그렇게 이런저런 고민을 하다가 역시나 오픈소스를 찾아보자는 생각이 들었다. 그리고 찾았다. pipe라는 패키지가 존재하더라. pip install pipe로 간단하게 설치할 수 있다.

 

내가 자주 사용하는 로또번호 생성기를 예제로 코드를 만들어보자.

from random import shuffle

numbers = list(range(1, 46))
shuffle(numbers)
print(numbers[:6])
# [31, 39, 18, 22, 10, 19]

기존의 방식대로는 이렇게 만들 수 있다. shuffle이라는 함수는 뒤에 받은 주소의 이터레이블한 자료형을 섞어주는 함수인데 return값이 없고 매개변수로 들어온 변수 그 자체를 섞는 것이다. 이따금씩 이런 함수를 만날 때마다 함수형 언어가 그리워진다.

 

Pipe 라이브러리를 사용하면 아래처럼 구현할 수 있다.

from random import shuffle
from pipe import Pipe


@Pipe
def shuffle_list(input_list: list):
    temp_list = input_list.copy()
    shuffle(temp_list)
    return temp_list


@Pipe
def take_slice(input_list: list, start_index, end_index):
    return input_list[start_index:end_index]


result_numbers = (
        list(range(1, 46)) |
        shuffle_list |
        take_slice(0, 6)
)
print(result_numbers)
# [39, 28, 6, 11, 15, 14]

ㅋㅋㅋㅋㅋ.. 그냥 쓰지 말자는 생각이 든다. 그래서 내가 제목에 발악이라고 적어둔 것이다.

 

사실 파이썬의 pipe 라이브러리가 로또 생성기의 예제에 쓰기엔 적절치 않은 느낌이 있다. 오히려 코드의 가독성을 해치고, 양을 늘리고, 의존성만 더해지는 최악의 적용형태라고 할 수 있다. 하지만 pipe그 자체는 훌륭한 라이브러리이다. pipe 라이브러리의 레포지토리인 https://github.com/JulienPalard/Pipe 에 가보면 시의적절한 예제를 볼 수 있다.

 

 

그럼에도 불구하고 elixir의 |>만큼의 아름다움에는 도달하기 힘든 것 같다. 요즘에는 DX라고 해서 User Experience처럼 Developer Experience가 업계의 주요 화두인데 fastAPI가 라우트를 읽어서 자동으로 생성해주는 swagger문서 + 간단한 비동기 라우트 생성 방법등등등을 통해 좋은 DX를 제공함으로써 flask의 자리를 빠르게 대체하고 있는 것처럼 말이다.

그런 면에 있어서 elixir의 꼬리재귀로 구현하는 순회함수나 pipe로 가독성 좋게 만들어주는 함수 같은 건 내 개인적으로 DX가 굉장히 좋았다. 그러니까 이걸 내가 업무를 하고 있는 python에서도 써보고 싶었던 것이고. 하지만 언어라는 태생적인 한계는 극복할 수가 없는 것 같다. 사실 위에 적은 pipe library 말고도 개개인이 작성한 함수형태도 있었고, 위에처럼 bulit-in method인 rshift를 사용하는 형태도 있었고 한데 적용시 그다지 유의미한 효과를 거둘 수 없을 것 같아서 제했다.

 

그래서 제목이 발악인 것이다. 끗.

 

 

 

 

Comments