2011-08-24 5 views
5

何らかの種類の状態を維持し、その状態によって異なる動作をする必要がある関数があるとします。 Felix Klings answer to another questionを少し変更したバージョンを使用して変更可能なデフォルト値 Python - 関数の属性または変更可能な既定値

を使用した関数属性

  • を使用して

    1. :状態は機能によって完全に格納されている場所、私はこれを実装するには、次の2つの方法の承知していますここでは、re.sub()で使用できる関数の例を示します。正規表現との3番目の一致が置き換えられるだけです:

      機能属性:

      def replace(match): 
          replace.c = getattr(replace, "c", 0) + 1 
          return repl if replace.c == 3 else match.group(0) 
      

      変更可能なデフォルト値:私に

      def replace(match, c=[0]): 
          c[0] += 1 
          return repl if c[0] == 3 else match.group(0) 
      

      最初はクリーンなようだが、私は、より一般的に第二を見てきました。どちらが好ましいのですか?

  • +4

    人々はちょうど機能を考えていないが、彼らがその広く初心者が使用していないので、属性が、_every_初心者が少なくとも一度は彼らにかまれますので、変更可能なデフォルト値を使用しての_everyone_思います。 – agf

    答えて

    4

    代わりに閉鎖を使用します。副作用はありません。ここで

    は(私がFelix Klings answerの元の例を単に変更した)例である:

    def replaceNthWith(n, replacement): 
        c = [0] 
        def replace(match): 
         c[0] += 1 
         return replacement if c[0] == n else match.group(0) 
        return replace 
    

    と使用方法:可変用

    # reset state (in our case count, c=0) for each string manipulation 
    re.sub(pattern, replaceNthWith(n, replacement), str1) 
    re.sub(pattern, replaceNthWith(n, replacement), str2) 
    #or persist state between calls 
    replace = replaceNthWith(n, replacement) 
    re.sub(pattern, replace, str1) 
    re.sub(pattern, replace, str2) 
    

    誰かが呼んで置き換える場合にどうするか(一致、c = [])?属性については

    あなたは、これらのアプローチの両方が危険な見て、私にカプセル化(はい私はPythonがデフ上の理由から、クラスで実装されていなかったことを知っている...)

    +0

    +1合意 - *元の*質問のコンテキストがネストされた関数であることを認識していませんでした。 – Seth

    +0

    @ JasonR.Coombs私は例を追加しました –

    +0

    ああ! 'c [0]'に 'c = [0]'を混乱させる... – n611x007

    1

    方法について:

    1. グローバル変数

    真を使用して、クラス

  • を使用し、これらは、関数内に完全に保存されていません。私はおそらくクラスを使用します。

    class Replacer(object): 
        c = 0 
        @staticmethod # if you like 
        def replace(match): 
         replace.c += 1 
         ... 
    

    getattrを使用し、あなたの実際の質問に答えるために。データを後で保存するための非常に明確で読みやすい方法です。あなたがやろうとしていることを誰かが読んでいるのはかなり明白です。

    デフォルトの可変引数バージョンは、一般的なプログラミングエラーの例です(毎回新しいリストを取得すると仮定します)。その理由だけで私はそれを避けるだろう。後でそれを読んでいる人は、その結果を十分に理解していないと良いアイデアだと判断するかもしれません。この場合でも、あなたの関数は一度しか機能しません(あなたのcの値は決してゼロにリセットされません)。

  • 1

    を破りました。問題は、クラスインスタンスのために叫んでいます。私たちは通常、コール間の状態を維持する機能については考えません。そのクラスはです。

    私はこの種のもののために以前は関数属性を使用していました。特に、他のコード内で定義されたワンショット関数(つまり、どこからでも使用することはできません)の場合は、新しいクラス全体を定義してそのインスタンスを作成するよりも、属性をタックする方が簡潔です。

    これは決してデフォルト値を悪用しません。デフォルト値の自然な目的は、引数のデフォルト値を提供することであり、呼び出し間の状態を維持することではないため、理解に大きな障壁があります。デフォルトの引数にはデフォルト以外の値を指定することができます。また、デフォルト値を悪用して状態を維持する関数を使用すると、通常は非常に奇妙な動作をします。

    2

    どちらの方法も私には奇妙に感じます。最初の方がはるかに優れています。しかし、あなたはそれをこのように考えます:「その状態と追加の入力で操作を行うことができる状態で何か」、それは本当に正常なオブジェクトのように聞こえる。そして、何かがオブジェクトのように聞こえるとき、それはオブジェクトでなければなりません... SO、私の解決策は__call__方法で単純なオブジェクトを使用することです:

    class StatefulReplace(object): 
        def __init__(self, initial_c=0): 
         self.c = initial_c 
        def __call__(self, match): 
         self.c += 1 
         return repl if self.c == 3 else match.group(0) 
    

    そして、あなたがグローバル空間で書くことができますかあなたのモジュールのinit:

    replace = StatefulReplace(0) 
    
    関連する問題