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

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

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

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

2回答

1341閲覧

モジュールのメソッド複数回の呼び出し(実行)を抑制したい。

NCC1701

総合スコア1680

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

0クリップ

投稿2018/02/09 00:23

前提・実現したいこと

スクリプトA.py、B.pyがあります。両方で共通する処理をモジュール化してC.pyとしました。C.pyの中にはc1とc2のメソッドがあり、A.py、B.pyはc1をなんども実行します。c1はc2を実行しますが、インタプリタの1回のセッションでc2は1度実行されれば十分な内容です(単にタプルをつくるだけなので)。

そこで、c2が一度呼び出されれば、次は参照するだけにとどめることができないでしょうか?(メモリーと実行速度、ネットアクセスを節約したい)

該当のソースコード

python

1# A.py または B.py 2import preprocess 3for sentence in sentence_list: 4 words = tokenize(sentence) #ここでtokenize()が呼び出される。

python

1# preprocess.py 上記のCに該当 2def get_stopwords(): # 上記のc2に該当 3 ''' 4 ストップワードリストを返す 5 ''' 6 import urllib.request 7 url = 'http://svn.sourceforge.jp/svnroot/slothlib/CSharp/Version1/SlothLib/NLP/Filter/StopWord/word/Japanese.txt' 8 with urllib.request.urlopen(url) as response: 9 stop_words = [w for w in response.read().decode().split('\r\n') if w != ''] 10 stop_words.extend(['れる', 'られる', 'する', 'いる', 'なる', 'ある', 'できる', 'おる']) 11 return tuple(stop_words) 12 13def tokenize(sentence): # 上記のc1に該当 14 import MeCab 15 import re 16 stopwords = get_stopwords() # ここが問題 17 mc = MeCab.Tagger('-Ochasen') 18 parsed =[] 19 pattern = r'\t名詞' 20 for morph in mc.parse(sentence).split('\n'): 21 if re.search(pattern, morph): 22 token = morph.split("\t")[2] 23 if token in stopwords: # ここでget_stopwords()を利用 24 continue 25 parsed.append(token) 26 return parsed 27

試したこと

url = 'http://svn.sourceforge.jp/svnroot/slothlib/CSharp/Version1/SlothLib/NLP/Filter/StopWord/word/Japanese.txt' with urllib.request.urlopen(url) as response: stop_words = [w for w in response.read().decode().split('\r\n') if w != '']

の部分をハードコーディングして

stop_words = 'あそこ あたり .... 同じ 感じ'.split()

するのも一案だと思っています。が、c2(get_stopword)が何度も呼ばれる点では違いが生じません。

また、A.py(B.py)側で、c2を呼び出し、変数に入れておくことも一案だと思っています。が、A.py側から見えなくても良いメソッドを利用するのが、スマートさに欠けるようにも思えます

python

1# A.py または B.py 2import preprocess 3stopwords = preprocess.get_stopword() 4for sentence in sentence_list: 5 words = tokenize(sentence, stopwords) #こんな感じ

補足情報(FW/ツールのバージョンなど)

python 3.5 miniconda

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

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

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

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

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

wakame

2018/02/09 00:54 編集

A.pyとB.pyのコードが同じように見えるのですが二つある意味はあるのですか。また、c2の処理は手動なりであらかじめダウンロードして前処理を加えたものをtxtなりで保存しておいて読み出すコードに替えてあければよいのではないでしょうか。
NCC1701

2018/02/09 01:23

A.py、B.pyは参考ですので、実際には上記のスクリプトの前後がA.pyとB.pyで異なります。Cが複数のスクリプトから呼び出されることを表現したかったため、誤解を生じさせてしまいました。
NCC1701

2018/02/09 01:26

txtなりで保存する場合も結果的にハードコーディングする場合とそれほど差がないように思えるのですが、違うものでしょうか?何度もディスクアクセスが生じるので、イメージなのですが、一度メモリ上に展開したのちは、ポインタ(参照)を使いまわすということで、ディスクアクセスの回数を減らせないかと思案しています。
guest

回答2

0

ベストアンサー

@functools.lru_cache

関数をメモ化用の呼び出し可能オブジェクトでラップし、最近の呼び出し最大 maxsize 回まで保存するするデコレータです。高価な関数や I/O に束縛されている関数を定期的に同じ引数で呼び出すときに、時間を節約できます。

以下サンプルコード

Python

1# -*- coding: utf8 -*- 2import functools 3 4 5@functools.lru_cache(maxsize=None) 6def get_stopwords() -> tuple: # 上記のc2に該当 7 ''' 8 ストップワードリストを返す 9 ''' 10 11 from time import sleep 12 # キャッシュ確認用のデバックスリープ! 13 sleep(3) 14 15 import urllib.request 16 url = 'http://svn.sourceforge.jp/svnroot/slothlib/CSharp/Version1/SlothLib/NLP/Filter/StopWord/word/Japanese.txt' 17 with urllib.request.urlopen(url) as response: 18 stop_words = [w for w in response.read().decode().split('\r\n') if w != ''] 19 stop_words.extend(['れる', 'られる', 'する', 'いる', 'なる', 'ある', 'できる', 'おる']) 20 return tuple(stop_words) 21 22 23def main() -> None: 24 for i in range(10): 25 print(get_stopwords()) 26 27 28if __name__ == '__main__': 29 main() 30

■余談
python 3.5以降なら関数の引数及び戻り値の型を明示するとIDE(統合開発環境)でヒント情報や型不一致の警告を出してくれます。

Python

1def get_stopwords() -> tuple:

投稿2018/02/09 02:38

編集2018/02/09 03:04
umyu

総合スコア5846

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

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

mkgrei

2018/02/09 02:48

これは便利ですね。
umyu

2018/02/09 02:55

>mkgreiさんへ かなり便利ですー。自作してたメモリキャッシュ機構がこれに置き換わりました。
NCC1701

2018/02/09 03:21

ありがとうございます。まさに求めていたものです。最近Pythonを使うようになりましたが、「コードを書ける」ということと「効率的、効果的なコードを書ける」のギャップを埋めるまでにはまだまだな感じです。
guest

0

クロージャに記録させるのいかがでしょう。

python

1def heavy_call(): 2 ans = [None] 3 def f(): 4 if ans[0] is not None: 5 return ans[0] 6 else: 7 import time 8 time.sleep(5) 9 ans[0] = 0 10 return ans[0] 11 return f 12 13f = heavy_call() 14for i in range(10): 15 print(f())

投稿2018/02/09 02:23

編集2018/02/09 02:30
mkgrei

総合スコア8560

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問