I. Intro

지난 번 WSL에서 Jupyter notebook을 사용하게 되었는데, 사실 Python은 여러 가지 패키지 상의 문제나 시스템 상의 이유로 다양한 버전을 다운로드 받아서 사용하고 있는 경우가 많습니다. 저의 경우에도 인제 막 시작한 Django를 혼자 쓸 때나 데이터 분석 툴들을 사용할 때에는 Python3.8을 사용하지만, 동아리에서 진행하는 Django sesison이나 Tensorflow를 건드려 볼때에는 3.8 버전이 아직 나오지 않아서 3.7 버전을 어쩔 수 없이 사용하게 됩니다. 그래서, 이 두 개의 버전을 Jupyter notebook에 사용하고 싶어서 처음에는 따로 따로 다운 받아서 사용하면 된다고 생각을 하였습니다.

 

python3.8 -m pip install jupyter notebook

python3.7 -m pip install jupyter notebook

 

이렇게 깔아주고 패키지 관리를 각각 파이썬 별로 해주면 됩니다. 근데, 이렇게 되었을 때 jupyter notebook에 가서 다음과 같은 명령어로 버전을 확인해주면,

 

import sys

sys.version

 

아마 처음에 깔린 python 버전만 나오지, python3.7 -m jupyter notebook이라는 명령어로 실행시키더라도 python3.8이 실행되는 분통터지는 모습을 볼 수가 있을 겁니다. 이러한 일이 일어나는 이유는 무엇이고, 어떤 방법으로 해결할 수가 있을까요?

 

II. 들어가기전 개념 정리

 

이상적인 패키지 정리

 

보통은 많은 패키지들은 위의 방식과 같이 각각의 Python version에 대해서 하나씩 깔리는 형태를 취하고 있습니다. 때문에 Python3.7 버전에서 다운 받은 django, pandas, numpy와 같은 패키지들이 3.8에서 사용되지 않는 것은 의존성이 각각의 버전에 맞추어져있고, 따로 따로 깔아서 사용해주어야 한다는 점으로 볼 수가 있을 겁니다.

Jupyter notebook은 어떨까요?

우리가 착각했던 관계

 

Jupyter notebook의 경우에도 pip를 사용하여 깔 수 있으나 조금은 다른 방식으로 접근을 해주어야한다는 것을 알 수가 있습니다. 바로 R과 같은 다른 언어에서도 기존의 Jupyter Notebook을 이용하여 사용할 수 있다는 점인데요. 아까의 패키지가 각각의 버전에 따로 깔려서 사용한다면 R에서는 작동이 되지 않아야하는데, R과 같은 다른 언어에서도 사용할 수 있다는 점이 살짝 이상하게 다가오긴 합니다. 때문에 관계는 다음과 같게 설정됨을 알 수가 있습니다.

 

진짜 관계

여기서 저희는 이런 프로그램 언어들이 돌아갈 수 있는 환경을 커널이라고 부르고 있습니다.

각각의 python3.7 python3.8 R과 같은 커널들이 존재를 하고 이 언어들을 Jupyter Notebook에 인식을 시켜서 우리는 Jupyter Notebook을 통해서 파일을 수정하고 실행을 해보게 됩니다. 때문에, python 다른 버전에서 설치를 해도, 이미 설치가 되어있는데, 뭘 설치를 하라는건지 컴퓨터에서는 아리송할 수 밖에 없는 것이죠. 만약 하나에 설치가 되어 있으면 커널 추가를 통해서 문제를 해결 할 수 있습니다.

 

III. 커널 추가

 

초창기 상태 입니다.

 

먼저 저의 상태는 python3이라는 python3.8의 버전이 깔려 있습니다. 여기에 3.7을 추가해보도록 하겠습니다.

 

입력할 명령어는

 

python3.7 -m pip insatll ipykernel

python3.7 -m ipykernel install --user --name="이름"

 

입니다. 이 때 추가를 원하는 버전의 python을 기준으로 앞의 python3.7 버전을 수정시켜주시면 되고, 이름의 경우에는 원하시는 인식명을 적어주시면 됩니다. 저의 경우에는 "Python(3.7)"로 하게 되었습니다.

이후 추가 되었다는 결과문이 나오게 되고 jupyter notebook을 다시 실행시켜주게 되면.

 

 

잘 설치 되었습니다.
시스템도 인식됩니다.

 

다만 Default로 설정되어있는 값이 존재를 하기 때문에, Jupyter notebook 파일을 열고나서는 커널을 기본 커널이 아닌 다른 커널로 변경을 해주셔야합니다.

'Programming > Python' 카테고리의 다른 글

Programming tip - Meta Programming  (0) 2020.04.04

I. Meta Programming?

인턴을 하면서 기본적인 알고리즘이나 여러 개념말고도 문제를 해결하면서 필요한 센스들이 많았습니다. 특히 C언어 배울 때 스쳐지나간 기본적인 Call by Reference, Call by Value 그리고 자료 구조와 알고리즘, 메모리 할당 등을 이용한 최적화 문제까지 컴퓨터 과학 커리큘럼을 따랐으면 기본적으로 세팅이 되어있을 개념이라 기초가 모자란 것에 대해서 많이 반성을 하게 되는 계기가 되었습니다. 하지만 프로그래밍에서 이러한 책에서 나오는 것이 아닌 다른 개념을 활용해야 쉽게 풀리는 문제들이 있습니다.

저같은 경우에는 프로세스와 쓰레드 개념을 익히면서 정말 긴 일을 하나의 무거운 일을 여러 작은 일들로 나누어서 컴퓨터가 가지고 있는 코어를 다 사용해야하는 방법을 찾아야만 했습니다. 근데 여러 일을 한 5가지로 나누는건 괜찮은데 100개 정도로 나눠서 작업을 하려하니

 

work1
work2
work3

 

이런식으로 변수를 100까지 붙혀주어야하는 것이 너무 번거롭기도 하고, 나중에 문제가 생겼을 때, 한꺼번에 고치기도 힘들더라고요. 그래서 이러한 불편함을 (찡찡거리면서) 어떻게 해결을 할까? 고민을 하는 도중에 저의 천사같은 선임 개발자 분께서

 

"그 문제는 String Implementation과 Meta Programming 을 잘 섞어서 쓰면 해결할 수 있을 것 같아요!"

 

라고 힌트를 주셨습니다.

 

이 개념의 경우에는 복잡도나 자료구조에 대한 문제도 아니고 자원을 어떻게 관리할까의 개념도 아니기 때문에 책에 절대로 나오지 않는 개념이지만 정말 유용한 개념이기도 합니다.

실제로 MIT에서 컴퓨터과학 클래스가 놓친 한 학기라는 개념에 들어간 내용이기도 합니다.

 

1/27 Meta Programming이 확실하게 들어가있습니다.

어떤 내용이길래 저의 고민을 한번에 해결하고 더 나아가서 다른 곳에서도 중요시하는 개념일까요?

 

II. 기존 프로그래밍의 한계

 

저희는 변수를 선언할 때, 변수의 이름을 먼저 넣고 그 다음에 변수에 어떤 값들을 넣을 것인가를 정해서 컴퓨터에 명령을 넣게 됩니다.

 

 

Example1

 

하지만 이러한 변수들을 100개 넘게 선언을 하려면 work1,work2,...work100까지 일일히 넣어야한다는 단점이 있습니다.

 

"그렇다면 for 문을 사용해서 쉽게 코드를 짜면 안되나요?"

 

 

Example2

이러한 시도는 막히기 마련인데, 변수를 지정할 때 완벽하게 끝까지 지정해준 상태에서 해주어야하지, 중간에 연산자가 들어가면 Python에서 인식을 할 수 없는 상태에 빠지게 됩니다. 이러한 문제는 Meta Programming을 통해서 해결할 수가 있습니다.

 

III. Meta Programming

1. eval 활용

이러한 Meta Programming의 가장 쉬운 방법은 eval을 활용하는 방법입니다.

String을 코드로 바꿔주어서 활용할 수가 있습니다. 

 

eval 활용 방법1

다음과 같이 따옴표 안에 들어간 string을 코드로 바꾸어서 작동을 하는 것을 알 수가 있습니다. 원래 work1에는 1이라는 값을 넣어주게 되었는데, eval 함수가 작동을 하면서 work1에는 11이라는 값이 들어간 가게 되었습니다. 이러한 점은 string을 쉽게 함수로 변형시켜서 활용을 할 수가 있는데요.

 

eval 활용 방법2

다음과 같이 여러 mass와 work의 뒤의 값들을 string의 결합 원리들을 이용하여 값을 바꿔나가면서 모든 변수들에 대한 코드 실행을 할 수 있음을 볼 수가 있습니다.

 

eval 활용방법 3

이러한 eval의 장점들에도 불구하고, eval 함수는 변수를 선언해주지 못한다는 단점이 있습니다. 이 때문에 저희가 목표로 하는 여러 변수들을 자동으로 생성해주는 목표에는 도달하지 못하였습니다. 이러한 점을 해결하기 위해서 저희는 exec 함수도 같이 활용해보도록 하겠습니다.

 

2. exec 함수

 

string의 값을 단순히 작동시키는 eval과 달리 exec는 Statement, 긴 문장을 실행시킬 수 있는 장점이 있습니다. 따라서, string안에 여러 연산자들이나 함수를 넣어서 작동도 가능하고, 그 안에 변수 선언도 가능이 하다는 장점이 있습니다. 이 때, Statement에 대해서 코드를 짜줄 때 문장단위로 짤 것이기 때문에 따옴표를 세개 써야할 것을 주의를 드리겠습니다.

 

exec 함수 작동 예시1

예시에서 보면 example_code라는 변수는 Statement로 이루어져있는 것을 알 수가 있는데, exec을 사용해서 이 코드를 돌렸을 때, if문에 따른 조건 판별 뿐만이 아니라 work_result에 대한 값도 같이 할당이 되면서 훌륭하게 string을 코드로 바꾸어서 작동이 된 것을 알 수가 있습니다.

그렇다면, 처음 고민인 work1,work2,...work100까지의 선언 문제도 이를 활용하여 해결 할 수가 있습니다.

 

 

exec 활용 예시

다음과 같이 직접적으로 저희가 work99와 work100을 선언을 해주지 않았는데도 exec함수를 이용해서 work 함수가 

여기서는 eval과 별 다를바 없이 코드를 짧게 짰지만, work 뒤에 들어가는 코드의 양이 길 경우에는 exec이 유용하게 사용될 수가 있습니다. 하지만 저희는 선언도 같이 해주어야하기 때문에 eval보다는 exec이 훨씬 더 적합한 선택임을 알 수가 있습니다.

저는 이러한 exec함수를 효율적으로 활용해서 work100까지 변수들을 알맞게 나누어 선언하여서 코어를 다 활용하여 일을 빠르게 끝낼 수가 있었습니다. Meta Programming을 활용하는 목적은 다르지만 일을 효율적으로 끝낼 수 있는 최고의 방법 중 하나라고 생각합니다. 

 

IV. 마치며

제가 마주친 문제를 어떻게 eval과 exec함수를 사용하여 해결했는지에 대해서 설명을 하게 되었습니다. 하지만 이러한 Meta Programming에는 이 둘의 함수를 활용한 방법 이외에도 Compile함수를 사용하여 문제를 해결하는 방법도 있습니다. 대략적인 함수의 형태는

example_code = compile('원하는 코드', '<string>', 'eval' 혹은 'single')

라는 형태로 사용이 됩니다. 여기서 eval과 single의 경우 eval은 하나의 줄안에서 끝날 수 있는 식, single은 여러 식으로 이루어져있는 Statement를 사용한다고 생각하시면 편할 것 같습니다.

이러한 compile 방법이 가장 깊고 세세하게 컨트롤 할 수 있는 방법이지만, 저는 eval과 exec으로 문제를 해결 할 수가 있어서 많이 활용하지는 않았습니다.

 

많은 양의 변수를 선언해야하거나, string 포멧을 코드로 바꿔서 사용하고 싶으면, Meta Programming에 도전해보시는 건 어떨까요?

'Programming > Python' 카테고리의 다른 글

Python 다양한 버전 Jupyter notebook에서 사용하기  (2) 2020.05.22

+ Recent posts