В пакете Python argparse при добавлении «formatter_class» MetavarTypeHelpFormatter «--help» больше не работает

Я использую пакет Python argparse, с простой конфигурацией

Когда я пытаюсь добавить в свой парсер formatter_class=argparse.MetavarTypeHelpFormatter, получаю ошибку:

AttributeError: 'NoneType' object has no attribute '__name__'

кто-нибудь еще сталкивался с этим? и может быть знаете почему?

это мой код:

import argparse

argParser = argparse.ArgumentParser(
    formatter_class=argparse.MetavarTypeHelpFormatter
)

argParser.add_argument("models", default='', nargs='+',
                       help = "Which model(s) to execute")


args = argParser.parse_args()
print(args)

Когда я пробую без добавления formatter_class, все работает. Он также работает с другими классами форматирования, например formatter_class=argparse.RawDescriptionHelpFormatter

Я также пробовал это с атрибутом prog и без него.

MetavarTypeHelpFormatter класс не получил широкого распространения. Это простая модификация базового форматтера. Как показывают ответы, проблема заключается в том, как реализовано значение по умолчанию type. Это прекрасно работает для других целей,
hpaulj 07.04.2024 16:48
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
1
77
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Краткий ответ: аргумент type для add_argument по умолчанию равен None, а не str.


Менее короткий ответ: тип None ведет себя как тип str, но не работает с MetaVarTypeHelpFormatter.

MetaVarTypeHelpFormatter требует явного типа для каждого аргумента.

argParser.add_argument("models", default='', nargs='+', type=str,
                   help = "Which model(s) to execute")

Его документированная цель — использовать имя атрибута type аргумента в качестве метапеременной, и по умолчанию этот атрибут имеет значение None.

Хотя тип аргумента по умолчанию — str, это просто означает, что значение str, взятое из списка аргументов, сохраняется, если атрибут type аргумента равен None, или преобразуется в заданный тип, если это не None.

>>> p = argparse.ArgumentParser()
>>> print(p.add_argument("foo").type)
None
>>> print(p.add_argument("foo", type=int).type)
<class 'int'>

Каждый синтаксический анализатор поддерживает реестр типов, что означает, что вы можете добавлять свои собственные произвольные сопоставления значений к вызываемым объектам для создания типов. Например,

>>> import argparse
>>> p = argparse.ArgumentParser()
>>> p.register('type', 'lowercase', lambda x: x.lower())
>>> p.add_argument("foo", type='lowercase')
_StoreAction(option_strings=[], dest='foo', nargs=None, const=None, default=None, type='lowercase', choices=None, required=True, help=None, metavar=None)
>>> p.parse_args(["HELLO WORLD"])
Namespace(foo='hello world')

Первоначально запись в реестре типов сопоставляется None с

def identity(string):
    return string

поэтому аргумент с «типом» None в противном случае ведет себя так, как если бы вы указали str явно.

Реестр типов не документирован и, за исключением этой единственной записи, больше не используется, поэтому неясно, почему он существует. Возможно, у автора были планы использовать его чаще, но они так и не осуществились. (Обратите внимание, что каждый парсер также имеет реестр действий, который используется чаще, поэтому вы можете указать действия, такие как 'append' и 'store_const', вместо прямого указания закрытых классов, таких как _AppendAction и _StoreConstAction.)

Вам нужно передать type= в add_argument:

MetavarTypeHelpFormatter использует имя аргумента type для каждого аргумент как отображаемое имя для его значений

Вы не уточнили, поэтому получается None.

Поскольку в вашем примере это тоже по умолчанию '', вы можете сделать:

argParser.add_argument(
    "models",
    default = "",
    nargs = "+",
    help = "Which model(s) to execute",
    type=str,
)

Это похоже на ошибку. Предполагается, что type по умолчанию имеет значение str для action по умолчанию, поэтому он должен вести себя так, даже если type опущен. Чрезвычайно глупо, если type=str можно и нужно опускать при обычных обстоятельствах (точно так же, как мы опускаем action='store', required=True и т. д.), но для MetavarTypeHelpFormatter это необходимо.

ShadowRanger 07.04.2024 15:25
argparse действительно гибко; Аргумент type может быть любым значением, а не только типом, а вызываемый объект, используемый для преобразования аргумента, ищется в реестре. По какой-то причине реестр сопоставляет None с функцией идентификации, а не просто сопоставляет str с str.
chepner 07.04.2024 15:27

(Непонятно, почему он такой гибкий; похоже, ничто другое не использует реестр типов, и он не задокументирован как что-то, что пользователи могли бы дополнять. Он похож на реестр действий, который сопоставляет, например, строку типа 'append' с классом _AppendAction, но гораздо реже используется.)

chepner 07.04.2024 15:29

Я предполагаю, что type был добавлен в реестр с самого начала, чтобы можно было добавить больше строк типов. Но, за исключением нескольких общих функций, таких как int и float, пользователи не используют множество функций специального типа. Позже кто-то добавил этот форматтер Metavar, но не протестировал его тщательно. Поскольку указать метавар так легко, я не уверен, что этот подкласс необходим (или часто используется). В любом случае, если мы вносим изменения, это должно быть связано с подклассом форматтера, а не с использованием реестра. parser._getvalue отлично справляется с реестром.

hpaulj 08.04.2024 00:00

Из документации Python:

MetavarTypeHelpFormatter использует имя аргумента типа для каждого аргумента в качестве отображаемого имени для его значений (вместо использования dest, как это делает обычный форматтер)

Итак, вам нужно добавить аргумент, например, как

p.add_argument('models', default='', nargs='+', help='no help', type=str) 

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

type по умолчанию None он же str отлично работает для синтаксического анализа. Просто этот подкласс Formatter использует слишком упрощенный подход к получению метавара по умолчанию. Я не думаю, что этот подкласс Formatter широко используется.
hpaulj 07.04.2024 18:06

С использованием nargs='+' проблем нет. Это с вашим тестовым примером. parse_args должен получить список строк, например. p.parse_args(['abc xyz']) или p.parse_args('abc xyz'.split())

hpaulj 07.04.2024 18:15

Вы правы - я забыл об этом.

mudskipper 08.04.2024 14:43

Я удалил комментарий о nargs='+' из своего предыдущего поста.

mudskipper 08.04.2024 14:50

p._get_value использует

type_func = self._registry_get('type', action.type, action.type)

который вернет str по умолчанию type.

Вместо этого этот подкласс Formatter использует

return action.type.__name__

чтобы получить тип, игнорируя _registry.

Я согласен, что использование _registry вместо type, вероятно, является недостаточно используемой функцией. реестр в основном используется для action. Если пользователям нужна настройка type, они могут определить необходимую функцию и использовать ее напрямую. Нет реальной необходимости добавлять его в реестр.

За все годы, что я следил за проблемами argparse, я не припомню, чтобы видел эту проблему раньше. Это говорит о том, что мало кто использовал MetavarTypeHelpFormatter, а если и использовал, то имел привычку использовать type=str. Установка metavar напрямую также является хорошей альтернативой.

Обратите внимание, что в формате по умолчанию default_metavar является производным от action.dest либо как есть, либо в верхнем регистре.

Другие вопросы по теме