ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Node with Python(python-shell)
    Tech/Development 2020. 7. 22. 19:25

    Node에서 Python을 사용해야할 상황이 생겨서 이것저것 서칭해봤는데, 꽤 괜찮은 라이브러리를 찾았다. python-shell이라는 라이브러리인데, 비교적 간편하고 쉽게 사용할 수 있었다. 나중에 또 필요한 일이 있을까봐. 사용법을 정리해두고자 한다.

     

    0. 라이브러리 설치하기


    # python-shell 설치
    npm install python-shell

     

    1. Python code 간단하게 실행하기(runString)


    # runString(code, options, callback)
    
    const { PythonShell } = require('python-shell')
    
    PythonShell.runString('x=1+1; print(x)', null, (err, msg) => {
    	console.log('err :', err)
        console.log('msg :', msg)
    })

    runString는 간단하게 Python Code를 실행시키는 것이다. 첫 인자에는 Python Code를 넣어주고, 이후에는 Options 값을 넣어주고, 마지막은 Callback하기 위한 함수를 넣어준다. err은 에러이고, msg는 Python Code에서 출력되는 값을 나타낸다. Options에 해당하는 것은 추후에 알아보도록 하자.

     

    일단, 위의 코드를 실행시키면 err : null, msg : ['2']라는 값이 출력된다. python-shell 라이브러리는 신기하게. print되는 값들을 callback을 통해 반환해줄 수 있다. 즉, Python을 통해 얻은 결과값을 Node에 가져오고 싶으면, Python Code 안에 해당 결과값을 print로 내보내주면 된다는 것이다. 그렇다면. 지금은 한 개의 값만 출력되었는데, N개의 결과값을 출력하고 싶으면 어떻게 할까?

    PythonShell.runString('x=1+1; print(x); y=2+2; print(y)', null, (err, msg) => {
    	console.log('err :', err)
        console.log('msg :', msg)
    })

    간단하게, print를 N번 넣어주면 된다. 그렇게 작성하면, 결과 값으로 배열의 형태로 결과값들을 출력해주는데, 위의 경우에는 msg : ['2', '4']를 출력하게 된다.

     

    2. Python Options & Arguments


    다음은 Options 값 세팅에 대해 알아보자. 우선, Options은 다음과 같다.

    let options = {
      mode: 'text',
      pythonPath: 'path/to/python', // Python의 경로를 나타낸다
      pythonOptions: ['-u'], // get print results in real-time
      scriptPath: 'path/to/my/scripts', // Python Script가 있는 경로
      args: ['value1', 'value2', 'value3'] // Python Script에 넘겨줄 인자 목록
    };

    조금 헷갈릴 수도 있는데. scriptPath는 실행시킬 Python Script가 있는 경로이다. 즉, 같은 위치라면, scriptPath는 './'을 설정해야하고, 굳이 해당 설정이

     

    조금 헷갈릴 수도 있는데, scriptPath는 실행시킬 Python Script가 존재하는 경로를 의미한다. 따라서, 같은 위치라면, './'로 정의해야한다. 그런데, 굳이 scriptPath 옵션을 사용 안 해도 된다. 해당 옵션 값을 지워주고, 그냥 PythonShell.run 함수를 실행시킬 때, 파일 경로를 입력하는 곳에 상대경로를 입력해주면 된다.

    // run(filePath, options, callback)
    
    PythonShell.run('./python.py', options, (err, msg) {
    	if (err) throw err;
        
        console.log('results: %j', msg);
    }

    위처럼 Options를 선언하고 PythonShell.run 함수를 실행시키면 된다. 그럼 또 궁금한게, Python Script에서는 이 인자를 어떻게 사용할 수 있을까 인데, sys.argv를 사용하면 된다.

    # Python Script
    
    import sys
    
    print(sys.argv[0], sys.argv[1], sys.argv[2], sys.argv[3])

    주의해야할 점은, sys.argv[0]의 경우에는 해당 Python Script의 파일명이 들어가고, [1]번부터 시작해 인자를 사용할 수 있다. 해당 Python Script를 Node에서 실행하면 결과 값으로, results: ['python.py value1 value2 value3']이 나오게 되는 것을 확인할 수 있다.

     

    3. Exchanging data between Node and Python


    마지막으로 Node와 Python이 data를 계속 교환해가며 코드를 짤 수도 있는데, 우선 Node에서 작성할 코드를 살펴보도록 하자.

    let pyshell = new PythonShell('python.py', options) // Options 설정 가능
    
    // stdin을 통해 Python Script에 변수 전달
    
    pyshell.send('hello')
    pyshell.send('world')
    
    // Python Script로부터 출력된 결과 값 받기(Python Script의 print에서 받아오는 값이 msg임)
    
    pyshell.on('msg', (msg) => {
    	console.log(msg)
    })
    
    
    // Python Script의 프로세스 종료하기
    
    pyshell.end((err, code, signal) => {
    	if(err) throw err;
      
        console.log('The exit code was: ' + code);
      	console.log('The exit signal was: ' + signal);
      	console.log('finished');
    })

     

    Python Script와 Node 사이에 데이터를 주고 받으며 사용해야하는 상황에 쓰면 좋은데, pyshell.send를 통해 변수를 전달할 수 있다. 물론, pyshell을 선언할 때 options 값을 설정하여 아까와 같이 고정 변수를 할당할 수도 있기도 하다. 그런데, 주의할 점은 options에 있는 args와 send를 통해 전달한 변수를 불러오는 방식이 다르다는 점이다. Options에 있는 것은, sys.argv로 불러오면 되고, Send를 통해 전달받은 변수는 sys.stdin으로 받아오면 된다.

    # Python Script
    
    import sys
    
    sys.stdout.write(sys.stdin.read())
    # 출력값
    # hello
    # world
    
    
    for line in sys.stdin:
    	print(line[:-1]) #[:-1]은 맨 오른쪽 값을 제외하고 모두를 의미
    # 출력값
    # hello
    # world    
    
       
    print(sys.argv[1])
    # 출력값
    # value1

    stdin을 사용할 때는 print외에도 sys.stdout을 통해 결과를 출력해줄 수도 있다. 또 print와 조합하여도 사용할 수 있다.

     

    참 편한 라이브러리들이 많은 것 같다. 공식문서도 잘 되어 있는 편이어서 좋았다(해당 라이브러리 git에 방문하면 이 외에의 기능들도 설명을 잘 해놓았다). 나중에는 나도 이런 편리한 라이브러리들을 만들어 올려보면 어떨까? 싶기도 하는데. 아직은 그럴만한 재주는 없는 것 같다. 이런 라이브러리를 만들어 내려면, 코드도 깔끔히. 효율적으로 짜고, 사용문서도 제작해야하니 많은 노력이 필요하겠지. 조금 더 노력해봐야겠다 느낀다.

    'Tech > Development' 카테고리의 다른 글

    Golang #2 - Method vs Function  (0) 2021.03.01
    Golang #1 - Map  (0) 2021.02.14
    Golang Reserved Words  (0) 2021.02.11
    Headless Browser, Puppeteer  (0) 2020.07.22
    카카오톡 챗봇 만들기 - 1  (3) 2020.04.11

    댓글 0

Designed by Tistory.