11

アクションを実装する組み込みのargparse._AppendActionのソースコードを見ていましたが、その実装方法に"append"戸惑いました。

    def __call__(self, parser, namespace, values, option_string=None):
        items = _copy.copy(_ensure_value(namespace, self.dest, []))
        items.append(values)
        setattr(namespace, self.dest, items)

それを分解するには:

  • _ensure_value属性のようなdict.setdefaultものです。つまりnamespace、名前の属性がある場合はself.destそれが返され、そうでない場合は設定され[]て返されます。
  • _copy.copy(x)浅いコピーだけを返します。がリストの場合x、それはまったく同じですlist(x)(ただし低速です)。
  • 次に、アイテムはから取得したリストのコピーnamespaceに追加されます。
  • 最後に、のself.dest属性がnamespaceコピーで上書きされます。これにより、古いリストがガベージコレクションされます。

なぜこのような回りくどい非効率的な方法で、各追加のリスト全体を破棄するのでしょうか。なぜこれで十分ではないのですか?

    def __call__(self, parser, namespace, values, option_string=None):
        items = _ensure_value(namespace, self.dest, [])
        items.append(values)
4

1 に答える 1

6

私は実装の専門家ではないので、(免責事項) これは単なる推測です。この実装により、ユーザーはargparse 内で変更されることなくdefault=...、 への呼び出しでリストを として渡すことができます。add_argumentおそらく、開発者は何らかの理由でこの種の安全性を望んでいたのでしょう。

あなたが言及する非効率性は、実際には大したことではありません。コマンドライン引数を解析するためのものであるため、この関数は、使用頻度が高い場合、プログラムごとに数十回しか呼び出されない可能性があります。


私はこれをテストしましたが、実際に、次のスクリプトを使用すると (場所argparse_tempは単にargparse.py現在のディレクトリにコピーされるので、それで遊ぶことができます):

import argparse_temp as argparse

lst = [1,2,3]
parser = argparse.ArgumentParser()
parser.add_argument('-l',default=lst,action='append')
print parser.parse_args()
print lst

これは出力します (次のように呼び出された場合: python test1.py -l 4):

Namespace(l=[1, 2, 3, '4'])
[1, 2, 3]

そのままでargparse、しかし:

Namespace(l=[1, 2, 3, '4'])
[1, 2, 3, '4']

提案された変更で。

によって返されるアクションを出力するとadd_argument、次のようになります。

_AppendAction(option_strings=['-l'], dest='l', nargs=None, const=None, default=[1, 2, 3, '4'], type=None, choices=None, help=None, metavar=None)

argparse が実装の他の場所でそのアクションに依存していると考えられるのはどれですか。default(ここでも変異していることに注意してください)。

于 2013-01-15T14:04:55.443 に答える