преобразование sys.argv в (*args, **kwargs)

Как правильно преобразовать аргументы командной строки в (*args, **kwargs) с помощью sys.argv? Есть некоторая функция:

def test(*a, **b):
    return print(a), print(b)

test('arg1', 'arg2', 'arg3', first='1', two='2', three='3')

Результат (все логично):

('arg1', 'arg2', 'arg3')
{'first': '1', 'two': '2', 'three': '3'}

Если попытаться передать те же аргументы через командную строку:

python3.8 code.py 'arg1', 'arg2', 'arg3', first='1', two='2', three='3'

и видоизменить саму функцию:

import sys

def test(*a, **b):
    return print(a), print(b)

test(' '.join(sys.argv[1:]))

python3.8 args_.py 'arg1', 'arg2', 'arg3', first='1', two='2', three='3'
('arg1, arg2, arg3, first=1, two=2, three=3',)
{}

Ответы (1 шт):

Автор решения: insolor

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

python3 code.py arg1 arg2 arg3 --first=1 --two=2 --three=3

Обзор различных библиотек для разбора параметров командной строки можно почитать в этой статье: Сравнение популярных CLI-библиотек для Python: click, cement, fire и другие

Простое "наколеночное" решение для вашего формата:

import sys
import ast

def test(*args, **kwargs):
    print(args)
    print(kwargs)


print(sys.argv[1:])
args = []
kwargs = {}
for item in sys.argv[1:]:
    item = item.rstrip(',')
    if item:  # Пустой элемент, например если была одиночная запятая
        try:
            args.append(ast.literal_eval(item))
        except (ValueError, SyntaxError):
            if "=" not in item:
                args.append(item)
            else:
                key, value = item.split("=", maxsplit=1)
                try:
                    kwargs[key] = ast.literal_eval(value)
                except ValueError:
                    kwargs[key] = value

test(*args, **kwargs)

Вывод:

$ python3 test_args.py 'arg1', 'arg2', 'arg3', first='1', two=2, three='3'
['arg1,', 'arg2,', 'arg3,', 'first=1,', 'two=2,', 'three=3']
('arg1', 'arg2', 'arg3')
{'first': 1, 'two': 2, 'three': 3}

Все кавычки при при передаче через командную строку потерялись, поэтому числа в кавычках стали просто числами. Если нужно передавать именно строки, можно упростить код убрав попытки конвертации при помощи ast.literal_eval (все параметры будут считаться строками):

import sys


def test(*args, **kwargs):
    print(args)
    print(kwargs)


print(sys.argv[1:])
args = []
kwargs = {}
for item in sys.argv[1:]:
    item = item.rstrip(',')
    if item:  # Пустой элемент, например если была одиночная запятая
        if "=" not in item:
            args.append(item)
        else:
            key, value = item.split("=", maxsplit=1)
            kwargs[key] = value

test(*args, **kwargs)

Вывод будет такой:

$ python3 test_args.py 'arg1', 'arg2', 'arg3', first='1', two=2, three='3'
['arg1,', 'arg2,', 'arg3,', 'first=1,', 'two=2,', 'three=3']
('arg1', 'arg2', 'arg3')
{'first': '1', 'two': '2', 'three': '3'}
→ Ссылка