Help us understand the problem. What is going on with this article?

複数pythonファイルを一つのpythonファイルにする

やりたいこと

例えば, main.pyが他のファイルを呼び出すようなプロジェクトを...

- main.py
- sub1.py
- folder
    - sub2.py

↓↓↓ 依存してる関数やクラスなどをちゃーんと...

onefile.py

ぎゅ!!!っとする.

(動機としては, プロジェクト管理しているpythonファイルを楽にgoogle colabで実行できないかな〜と思ったからです.)

解決法

こちら, stickytapeというpythonパッケージを使えばOK.
https://pypi.org/project/stickytape/

stickytape = 粘着テープ

実験

実験したコードは全てGithubに公開してますので, 参考までに.

install

これでstickytapeコマンドが使えるようになる. (コマンド名なげぇ...)

$ pip install stickytape

ファイルを用意

以下のような構成とします.

- main.py
- sub1.py
- folder
    - sub2.py

folder/sub2.py

適当にAppleクラスとか作って, 適当にvalueプロパティとか持たせときます.

class Apple:

    def __init__(self, value):
        self.value = value

sub1.py

適当に平均関数とか作っときます.

sub1.py
def mean(a, b):
    return (a+b)/2

main.py

importしてきて適当に計算させて適当に表示します.

from sub1 import mean
from folder.sub2 import Apple

apple1 = Apple(value=100)
apple2 = Apple(value=200)

result = mean(apple1.value, apple2.value)
print(result)

いざ!一つのファイルに!

以下のコマンドを実行します. (もちろんonefile.pyのところは何でもOK)

$ stickytape main.py > onefile.py

結果

以下のようなonefile.pyが生成されます.

#!/usr/bin/env python


import contextlib as __stickytape_contextlib

@__stickytape_contextlib.contextmanager
def __stickytape_temporary_dir():
    import tempfile
    import shutil
    dir_path = tempfile.mkdtemp()
    try:
        yield dir_path
    finally:
        shutil.rmtree(dir_path)

with __stickytape_temporary_dir() as __stickytape_working_dir:
    def __stickytape_write_module(path, contents):
        import os, os.path

        def make_package(path):
            parts = path.split("/")
            partial_path = __stickytape_working_dir
            for part in parts:
                partial_path = os.path.join(partial_path, part)
                if not os.path.exists(partial_path):
                    os.mkdir(partial_path)
                    open(os.path.join(partial_path, "__init__.py"), "w").write("\n")

        make_package(os.path.dirname(path))

        full_path = os.path.join(__stickytape_working_dir, path)
        with open(full_path, "w") as module_file:
            module_file.write(contents)

    import sys as __stickytape_sys
    __stickytape_sys.path.insert(0, __stickytape_working_dir)

    __stickytape_write_module('sub1.py', 'def mean(a, b):\n    return (a+b)/2')
    __stickytape_write_module('folder/sub2.py', 'class Apple:\n\n    def __init__(self, value):\n        self.value = value')
    from sub1 import mean
    from folder.sub2 import Apple

    apple1 = Apple(value=100)
    apple2 = Apple(value=200)

    result = mean(apple1.value, apple2.value)
    print(result)

一瞬「何じゃこりゃ!?」となりましたが, これを実行すると...

150.0

無事正しい計算結果が表示されました.

Google Colabで実験

先ほどのコードをGoogle Colabにコピペして実行しました.

下のように, 無事150.0が表示されました.

(虹色の猫が歩いてるのは気にしないでください.)

スクリーンショット 2020-06-25 12.38.44.png

スクリプト化

ここからは余談です.

stickytapeというコマンドは長いですし, いちいち生成ファイルのディレクトリを指定するのとか怠いので, 次のようにスクリプト化しておくといいかと思います.

- main.py
- sub1.py
- folder
    - sub2.py
- scripts
    - tape.sh
- build
    - onefile.py
tape.sh
# 初期値
entry="main.py"
output="onefile.py"

# オプション
while getopts e:o: OPT
do
    case $OPT in 
        "e" ) entry=${OPTARG};;
        "o" ) output=${OPTARG};;
    esac
done

# 実行
stickytape ${entry} > "build/${output}"

以下のコマンドで, main.pyを実行し, buildディレクトリにonefile.pyを生成してくれます.

$ sh scripts/tape.sh

オプションも用意しておきました.

オプション名 説明
-e エントリポイントのファイル名
-o アウトプットするファイル名
$ sh scripts/tape.sh -e <ファイル名> -o <ファイル名>

生成されるディレクトリはbuildで固定にしているので, 嫌なら勝手に変えてください.

自己紹介

冒頭に書くと邪魔になるので最後にひっそりと自己紹介させてください。

名前 綿岡晃輝
学校 神戸大学大学院
学部の研究 機械学習, 音声処理
大学院の研究 機械学習, 公平性, 生成モデル, etc
Twitter @Wataoka_Koki

Twitterフォローしてね!

wataoka
専門は機械学習の公平性です
https://twitter.com/wataoka_koki
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした