18

pythonicオブジェクト指向に完全に移行せずに (最適化などの目的で) 状態を維持する方法はありますか?

私の質問をよりよく説明するために、JavaScript で頻繁に使用するパターンの例を次に示します。

var someFunc = (function () {
    var foo = some_expensive_initialization_operation();
    return someFunc (bar) {
        // do something with foo and bar
    }
}());

外部的には、これは他の関数と同じで、オブジェクトなどを初期化する必要はありませんが、クロージャにより値を 1 回計算することができ、それを基本的に定数として使用できます。

Python でのこの例は、正規表現を最適化する場合です。re.compileコンパイルされたバージョンをmatchおよびsearch操作に使用して保存すると便利です。

Python でこれを行う唯一の方法は、モジュール スコープで変数を設定することです。

compiled_regex = compile_my_regex()

def try_match(m): # In reality I wouldn't wrap it as pointlessly as this
    return compiled_regex.match(m)

または、クラスを作成して:

class MatcherContainer(object):
    def __init__(self):
        self.compiled_regex = compile_my_regex()
    def try_match(self, m):
        self.compiled_regex.match(m)

my_matcher = MatcherContainer()

前者のアプローチはアドホックであり、関数とその上で宣言された変数が互いに関連付けられているかどうかはあまり明確ではありません。また、モジュールの名前空間を少し汚すことにもなりますが、これにはあまり満足していません。

後者のアプローチは冗長に見え、ボイラープレートには少し重いようです。

これに対処するために私が考えることができる唯一の他の方法は、このような関数を別のファイル (モジュール) に分解し、関数をインポートするだけで、すべてがクリーンになります。

これに対処する方法について、より経験豊富なPythonersからのアドバイスはありますか? それとも、気にせずに問題を解決することに専念しますか?

4

6 に答える 6

15

デフォルトの引数を使用してこれを行うこともできます。

def try_match(m, re_match=re.compile(r'sldkjlsdjf').match):
    return re_match(m)

デフォルトの引数は、モジュールのインポート時に一度だけ評価されるためです。

またはさらに簡単です:

try_match = lambda m, re_match=re.compile(r'sldkjlsdjf').match: re_match(m)

またはまだ最も簡単です:

try_match = re.compile(r'sldkjlsdjf').match

これにより、再コンパイル時間 (実際には re モジュール内で内部的にキャッシュされる) だけでなく、「.match」メソッドのルックアップも節約されます。ビジー関数またはタイト ループでは、これらの '.' 解決策を追加できます。

于 2012-08-08T14:27:33.727 に答える
14

JavaScript でクロージャーを定義するのと同じ方法で、Python でクロージャーを定義できます。

def get_matcher():
    compiled_regex = compile_my_regex()

    def try_match(m)
        return compiled_regex.match(m)

    return try_match

compiled_regexただし、Python 2.x では、クロージャーは読み取り専用です (上記の例のように、関数呼び出し内に再割り当てすることはできません)。クロージャー変数が変更可能なデータ構造 ( listdict、 などset) である場合は、関数呼び出し内で変更できます。

def get_matcher():
    compiled_regex = compile_my_regex()
    match_cache = {}

    def try_match(m):
        if m not in match_cache:
           match_cache[m] = compiled_regex.match(m)

        return match_cache[m]

    return try_match

Python 3.x では、キーワードを使用してnonlocal、関数呼び出しでクロージャー変数に再割り当てできます。(PEP-3104)

Python でのクロージャに関する次の質問も参照してください。

于 2012-08-08T14:27:35.190 に答える
8

どうですか

def create_matcher(re):
    compiled_regex = compile_my_regex()
    def try_match(m):
        return compiled_regex.match(m)
    return try_match

matcher = create_matcher(r'(.*)-(.*)')
print matcher("1-2")

?

しかし、ほとんどの場合、クラスはより良く、よりクリーンです。

于 2012-08-08T14:18:57.787 に答える
5

任意の関数で属性をスタッシュできます。関数名はグローバルなので、他の関数で取得できます。例えば:

def memorize(t):
    memorize.value = t

def get():
    return memorize.value

memorize(5)
print get()

出力:

5

これを使用して、状態を単一の関数に保存できます。

def memory(t = None):
    if t:
        memory.value = t
    return memory.value

print memory(5)
print memory()
print memory()
print memory(7)
print memory()
print memory()

出力:

5
5
5
7
7
7

確かに、その有用性は限られています。この質問では、SOでのみ使用しました。

于 2012-08-09T02:22:25.807 に答える
2

よく使用される規則は、プライベート モジュール レベル グローバルの前にアンダースコアを付けて、それらがモジュールのエクスポートされた API の一部ではないことを示すことです。

# mymodule.py

_MATCHER = compile_my_regex()

def try_match(m):
    return _MATCHER.match(m)

これを行うことを思いとどまらせるべきではありません。関数クロージャの隠し変数よりも望ましいことです。

于 2012-08-08T14:43:26.127 に答える