brunch

You can make anything
by writing

C.S.Lewis

by 유윤식 Aug 06. 2024

Python: Maturin#04

#serde_json #dict

너무 큰 JSON 을 파이썬에서 처리할 때 고민.


너무 느리다.

메모리를 너무 많이 먹는다.


그래서 orjson 라이브러리를 사용하곤 하는데


결국 json 데이터를 핸들링하는 주체는 파이썬이고

키-값 데이터를 loop 하면서 뭔가 조작을 한다는건 부담일 수 있다.


이걸 Rust 로 보내서 동시성을 확보면 어떨까.


# maturin project

>> maturin new calc_test

>> cd calc_test


이러면 정확히 아래와 같은 트리구조의 디렉토리가 생성된다.


Cargo.toml 을 먼저 살펴보고,


[package]

name = "calc_test"

version = "0.1.0"

edition = "2021"


# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[lib]

name = "calc_test"

crate-type = ["cdylib"]


[dependencies]

pyo3 = "0.21.1"

serde = { version = "1.0", features = ["derive"] }

serde_json = "1.0"


디펜던시를 잘 봐둬야 한다.


다음으로 pyroject.toml 을 보면,


[build-system]

requires = ["maturin>=1.6,<2.0"]

build-backend = "maturin"


[project]

name = "calc_test"

requires-python = ">=3.8"

classifiers = [

    "Programming Language :: Rust",

    "Programming Language :: Python :: Implementation :: CPython",

    "Programming Language :: Python :: Implementation :: PyPy",

]

dynamic = ["version"]

[tool.maturin]

features = ["pyo3/extension-module"]



이 또한 자동으로 완성된다.


이제 로직을 하나 만들건데, src/lib.rs 에서 진행한다.

json 형태의 string 데이터를 가져와서

특정 키를 이용해 값을 취하고

이를 이용해 간단한 덧셈 로직을 구현해본다.



use pyo3::prelude::*;

use pyo3::exceptions::PyValueError;

use serde_json::Value;


#[pyfunction]

fn calculate_digit(json_data: &str, key1: &str, key2: &str) -> PyResult<f64> {

    let data: Value = serde_json::from_str(json_data).unwrap();

    

    let value1 = data[key1]

    .as_f64()

    .ok_or_else(|| {

        PyValueError::new_err(format!("Error! {} : Key '{}' not found or not a number", key1, key1))

    })?;


    let value2 = data.get(key2)

    .and_then(

        |value| {

            value.as_f64()

        }

    )

    .ok_or_else(

        || {

            PyValueError::new_err(format!("Error! {} : Key '{}' not found or not a number", key2, key2))

        }

    );


    let rate = (value1 + value2.unwrap()) / 2.0;


    Ok(rate)

}


#[pymodule]

fn calc_test(m: &Bound<'_, PyModule>) -> PyResult<()> {

    m.add_function(wrap_pyfunction!(calculate_digit, m)?)?;

    Ok(())

}


보는것처럼 간단하다.

키로 값을 받아서 덧셈 & 나눗셈을 하고 그 결과를 반환한다.


다음 명령어로 whl 파일을 만든다.


>> maturin build --release


이러면 target 디렉토리에 whl 파일이 생성되는데

해당 위치를 찍고

바로 pip 로 설치를 한다.


예를 들어,

>> pip install /path/calc_test/target/wheels/calc_test-0.1.0-cp38-cp38-macosx_10_12_x86_64.whl --force-reinstall


이러면 설치는 완료!


바로 쥬피터 꺼내서


import os

import orjson

import json

from calc_test import calculate_digit


large_json_data = {

    "key1": 5.0,

    "key2": 3.0,

    "key3": 5.0,

    "key4": 3.0,

    "key5": 5.0,

    "key6": 3.0,

}


# json_str = json.dumps(large_json_data)


json_str = orjson.dumps(large_json_data).decode()


try:

    rate = calc_test.calculate_digit(json_str, "key1", "key2")

    print(f"Calculated rate: {rate}")

except ValueError as e:

    print(f"Error calculating rate: {e}")



이러면 거~~대한 json 파일 핸들링도 좀 더 가볍게 할 수 있다.

브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari