質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

4回答

4476閲覧

同じ名前空間の中でファイル分割したい

GlassGrass

総合スコア52

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

3グッド

3クリップ

投稿2019/03/28 03:59

編集2019/03/28 08:38

ソース管理のため、名前空間とファイル構成の両面でプロジェクトの階層化をしたいと考えています。

ファイル構成:

hogepackage +- __init_.py +- foo.py +- bar.py

実現したい名前空間とクラスの構成:

namespace hogepackage +- <init module> +- class Foo +- class Bar

しかし、Pythonではパッケージ(フォルダ)とモジュール(__init__以外のファイル)がそれぞれ名前空間の階層となるので、ファイル構成をそのまま適用すると以下のように余計な名前空間ができてしまいます。

namespace hogepackage +- <init module> +- namespace foo | +- class Foo +- namespace bar +- class Bar

一般には__init__.pyの中でクラスをインポートすることで、利用側ではあたかもパッケージ直下にクラスがあるかのように見せかけることができますが、Pythonの型システム上では依然としてモジュール層が残っています。

hogepackage/__init__.py:

Python

1#!/usr/bin/env python3 2from .foo import Foo 3from .bar import Bar
>>> from hogepackage import Foo >>> type(Foo()) <class 'hogepackage.foo.Foo'>

これはリフレクション上の混乱を招きますし、sphinxのようなドキュメントツールもこの階層のままドキュメントを生成してしまいます。

一方、以下のようにすることで型システム上でも期待通りのクラス構成を実現できることが分かりました。

hogepackage/__init__.py:

python

1#!/usr/bin/env python3 2import sys 3import os 4import codecs 5 6dir = os.path.dirname(os.path.abspath(__file__)) 7with codecs.open(dir + '/foo.py', 'r', 'utf-8'): as file: 8 exec(''.join(file.readlines())) 9with codecs.open(dir + '/bar.py', 'r', 'utf-8'): as file: 10 exec(''.join(file.readlines()))
>>> from hogepackage import Foo >>> type(Foo()) <class 'hogepackage.Foo'>

ただ、これはあまりにも強引な解決法のように見えます。
もっとよい解決方法はないでしょうか?

t_obara, magichan, tachikoma👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tachikoma

2019/03/28 04:31

どうするのがいいのかすぐ分かりませんが、例えばpandasのDataFrameは何もしてなかったりしますね。 >>> import pandas >>> type(pandas.DataFrame()) pandas.core.frame.DataFrame
hayataka2049

2019/03/28 05:38

割と何もしないのが普通なんじゃと思い始めました。 >>> from sklearn.svm import SVC >>> SVC <class 'sklearn.svm.classes.SVC'> sphinxはsphinx側でうまいこと設定できないのか・・・
t_obara

2019/03/28 05:43

確かにDataFrameなど参考になりますよね。C/C++のような一ファイル一クラスといった認識ではなく、一つのpy(foo/barなど)はnamespaceでまとめられた機能群と考えるのがPython流なのではないかと。(なので、解決方法というよりはそれに合った構成にすべきではという意図になります) ただ、一ファイルが大きくなる傾向が出るので、質問されたように適切に分離したくなるかとは思いますが、その場合も機能毎に分割するようなリファクタリングをするのが良いのではないかと。
guest

回答4

0

ベストアンサー

リフレクション上の混乱を招きます

気持ちはわかるのですが、Pythonを使っている限り、サードパーティーのライブラリを使っていて頻繁に起こることなので(tachikoma さんの補足の通り)自分が書くプログラムで気にしていてもしょうがないと思いました。


さて。

https://docs.python.org/ja/3.6/reference/datamodel.html#index-34

なんと__module__属性は書き換え可能(!)なので、クラスを定義したあとで差し替えてしまうと、別のモジュールに属させることが可能です。

hogepackage/_ init _.py

python

1from .bar import Bar 2from .foo import Foo

hogepackage/foo.py

python

1class Foo: 2 def __init__(self) -> None: 3 super().__init__() 4 self.data = 'Foo' 5 6Foo.__module__ = 'hogepackage'

hogepackage/bar.py

python

1class Bar: 2 def __init__(self) -> None: 3 super().__init__() 4 self.data = 'Bar' 5 6Bar.__module__ = 'hogepackage'

main.py

python

1#!/usr/bin/env python 2import pickle 3from hogepackage import Foo, Bar 4 5 6def main(): 7 f = Foo() 8 b = Bar() 9 10 print(f) 11 print(type(f)) 12 print(pickle.dumps(f)) 13 print(pickle.loads(pickle.dumps(f))) 14 print() 15 print(b) 16 print(type(b)) 17 print(pickle.dumps(b)) 18 print(pickle.loads(pickle.dumps(b))) 19 20 21if __name__ == '__main__': 22 main()

結果

plain

1<hogepackage.Foo object at 0x104d9f358> 2<class 'hogepackage.Foo'> 3b'\x80\x03chogepackage\nFoo\nq\x00)\x81q\x01}q\x02X\x04\x00\x00\x00dataq\x03X\x03\x00\x00\x00Fooq\x04sb.' 4<hogepackage.Foo object at 0x104e01be0> 5 6<hogepackage.Bar object at 0x104ddf400> 7<class 'hogepackage.Bar'> 8b'\x80\x03chogepackage\nBar\nq\x00)\x81q\x01}q\x02X\x04\x00\x00\x00dataq\x03X\x03\x00\x00\x00Barq\x04sb.' 9<hogepackage.Bar object at 0x104e01a20>

私自身は、他人が見て何のためにやっているのかわからないコードや、どんな副作用があるか考えるのが面倒なコード、将来にわたって動作するか怪しいコードを差し挟むのは気持ち悪いと思います。
それに対して見合うメリットは感じません。

投稿2019/03/28 05:35

quickquip

総合スコア11038

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

GlassGrass

2019/03/30 09:47

pythonの流儀は別にして、技術的な解決手順としてはこれが一番無難みたいですね。 とはいえやっぱり流儀には従ったほうがいいとも思うので実用に関しては再考してみます。
guest

0

こんなやり方でいかがでしょうか。

Python2

1import imp 2 3imp.load_source('hogepackage', 'foo.py') 4imp.load_source('hogepackage', 'bar.py') 5 6from hogepackage import Foo 7print(type(Foo()))

結果

<class 'hogepackage.Foo'>

Deprecation Warningが出るとのことですので、importlibを使った場合

Python3

1from importlib.machinery import SourceFileLoader 2 3 4SourceFileLoader('hogepackage', 'foo.py').load_module() 5SourceFileLoader('hogepackage', 'bar.py').load_module() 6 7from hogepackage import Foo, Bar 8print(type(Foo())) 9print(type(Bar()))

でも、名前空間の管理のためにやるのはやめたほうがいいでしょうね。確認していませんがPyCharmなどIDEの支援をあえて放棄することになるでしょうし、静的解析をするツールも動くと思えません。プラグインとかダイナミックにソースをロードしたいとか、フレームワークを作るとか特殊な用途です。

投稿2019/04/02 01:25

編集2019/04/02 01:50
Kenji.Noguchi

総合スコア358

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tachikoma

2019/04/02 01:32

こういう方法もあるんですね。impはDeprecation Warningが出てしまうので、importlibで同様のことができるとなおよさそうですね。
guest

0

namespaceを同じにするのではダメなんでしょうか

投稿2019/03/28 04:11

y_waiwai

総合スコア87774

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hayataka2049

2019/03/28 04:23

それをどうやって実現すればいいか?(そもそもできるのか?)という質問だと思いますが。
_Victorique__

2019/03/28 04:30

同意ですね。 namespaceが本来の意味をなしていない気がします。
yuba

2019/03/28 05:21

C#ならばできるのですが、Pythonではディレクトリファイルツリー構造が名前空間ですからね。
Kenji.Noguchi

2019/04/02 01:21 編集

(コメント削除。趣旨を勘違いしました)
guest

0

これはリフレクション上の混乱を招きますし、sphinxのようなドキュメントツールもこの階層のままドキュメントを生成してしまいます。

私だったら、これらのデメリットは目をつむって当初のやり方で実装します。

が、あえて手を加えるのであれば、

python

1# hogepackage/__init__.py 2from . import foo 3 4class Foo(foo.Foo): 5 pass

というように、継承クラスを作ってはいかがですか。
sphinxは使った事がないのでどのようにドキュメントが作成されるかはわかりませんが、継承情報ぐらいはなんとかしてくれる。…といいなぁ…。

投稿2019/03/28 15:13

katsuko

総合スコア3469

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問