Объясните, пожалуйста, объяснение фрагмента кода из модуля argparse

Я смотрел исходный код встроенного 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)

Ответ 1

Я не эксперт в реализации, поэтому (отказ от ответственности) это действительно просто догадка. С помощью этой реализации пользователь может передать список как default=... в вызове add_argument, не изменяя его в argparse. Возможно, этот тип безопасности был желателен разработчиками по той или иной причине.

Неэффективность, которую вы упоминаете, действительно не имеет большого значения. Это для анализа аргументов командной строки, поэтому эта функция, скорее всего, называется только 10 раз для каждой программы при интенсивном использовании.


Я тестировал это и действительно, если я использую следующий script (где 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).