我正在查看内置argparse._AppendAction的源代码,该代码实现"append"
操作,并对way it is implemented感到困惑:
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
的{{1}}属性被副本覆盖,这会导致旧列表被垃圾收集。为什么会以这样一种迂回而低效的方式,为每个附加物扔掉一个完整的清单?为什么这不够?
namespace
答案 0 :(得分:6)
我不是实施方面的专家,所以(免责声明)这只是一个猜测。通过此实现,用户可以在default=...
的调用中将列表作为add_argument
传递,而不会在argparse中进行变更。也许这种安全性是开发人员出于某种原因所希望的。
你提到的效率低下并不是什么大问题。它用于解析命令行参数,因此在大量使用时,此函数可能仅被调用每个程序10次。
我已经测试了这个,事实上,如果我使用以下脚本(其中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
也已发生变异。)