python распаковка списка аргументов с * args и ** kwargs вместе с другими позиционными и ключевыми параметрами - PullRequest
2 голосов
/ 14 июля 2020

У меня есть следующая функция, определенная в файле с именем kwargs.py. (изменение модуля на unpacking.py заставило функцию работать должным образом). См. Изображение внизу вопроса.

def tag(name, *content, cls=None, **attrs):
    if cls is not None:
        attrs['class'] = cls
    
    if attrs:
        attr_str = ''.join(' %s="%s"' % (key,val) 
                                         for key, val 
                                         in sorted(attrs.items()))
    else:
        attr_str = ''
    
    if content:
        return '\n'.join('<%s%s>%s</%s>' % (name,attr_str,c,name) for c in content)
    else:
        return '<%s%s />' % (name,attr_str)

при выполнении этого

tag('div', 'testing', cls='test', **dict(id=33,alt='this is a test'))

Результат, который я получаю:

'<div alt="this is a test" class="test" id="33">testing</div>'

Но когда я выполняю это

tag(**dict(name='div', content=('testing','and testing'), cls='test', id=33, alt='this is a test'))

я получаю только

'<div alt="this is a test" class="test" id="33" />'

почему назначается параметр name, а не content. (Даже если кортеж не распакован, я ожидал, что хотя бы сам кортеж будет назначен на content [0]).

Что мне здесь не хватает?

Изменить: Python 3,8,3 x86 дюйма Windows 10 введите описание изображения здесь

Ответы [ 2 ]

2 голосов
/ 14 июля 2020

Сначала убедитесь, что мы используем ту же терминологию:

  • Функция объявляет параметры (name, *content, cls=None, **attrs в вашем примере),
  • Функция вызов получает аргументы (например, 'div', 'testing', cls='test', **dict(id=33, alt='this is a test')).

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

Сначала посмотрим на сигнатуру функции:

def tag(name, *content, cls=None, **attrs):
    ...

Эта функция определяет четыре параметра:

  • name - параметр позиционного или ключевого слова,
  • content - параметр varargs, который захватывает любое количество дополнительных позиционных аргументов,
  • cls - параметр только для ключевых слов со значением по умолчанию,
  • attrs - параметр-ключевое слово, который захватывает любое количество дополнительных ключевое слово аргументов.

Когда вы вызываете эту функцию следующим образом, происходит следующее:

tag('div', 'testing', cls='test', **dict(id=33, alt='this is a test'))
  • 'div' привязан к name,
  • 'testing' захватывается content resulti ng в 1-кортеже,
  • 'test' привязан к cls,
  • id=33, alt='this is a test' захватываются attrs.

Теперь Особенностью параметров *content и **attrs является то, что они захватывают любое количество лишних аргументов, но не могут быть связаны напрямую. Т.е. привязать content=(1, 2) нельзя. Вместо этого, если вы передадите tag('foo', 1, 2), эта привязка произойдет автоматически. Итак, если вы вызываете функцию следующим образом:

tag(**dict(name='div', content=('testing', 'and testing'), cls='test', id=33, alt='this is a test'))

, тогда все аргументы предоставляются ключевым словом и, следовательно, все, кроме name и cls, захватываются attrs. Это потому, что *content захватывает только позиционные аргументы.

2 голосов
/ 14 июля 2020

*content не является аргументом. Вы не можете ничего присвоить ему.

Документы действительно не объясняют это явно. Это просто переменная, которую вы можете использовать в теле функции. Что он делает, так это «собирает все оставшиеся входные аргументы».

Обычно эти переменные c аргументы будут последними в списке формальных параметров, потому что они coop вверх все оставшиеся входные аргументы, которые передаются функции.

https://docs.python.org/3/tutorial/controlflow.html#arbitrary -argument-lists

Эта функция

def func(*args, **kwargs):
    print("args", args)
    print("kwargs", kwargs)


func(args=[1, 2, 3])

напечатает

args ()
kwargs {'args': [1, 2, 3]}

РЕДАКТИРОВАТЬ:

Ваши примеры неверны. Вы не можете получить другой результат.

Для этой функции

def tag(name, *content, cls=None, **attrs):
    if cls is not None:
        attrs['class'] = cls
    if attrs:
        attr_str = ''.join(' %s="%s"' % (key, val)
                           for key, val
                           in sorted(attrs.items()))
    else:
        attr_str = ''

    if content:
        resp = '\n'.join('<%s%s>%s</%s>' % (name, attr_str, c, name) for c in content)
    else:
        resp = '<%s%s />' % (name, attr_str)

    print("name", name)
    print("*content", content)
    print("cls", cls)
    print("attrs", attrs)
    print("attrs_str", attr_str)
    print("resp", resp)
    print('-'*10)
   
    return resp

Если вы запустите это

tag('div', 'testing', cls='test', **dict(id=33, alt='this is a test'))
tag(**dict(name='div', content=('testing','and testing'), cls='test', id=33, alt='this is a test'))

, вы получите

name div
*content ('testing',)
cls test
attrs {'id': 33, 'alt': 'this is a test', 'class': 'test'}
attrs_str  alt="this is a test" class="test" id="33"
resp <div alt="this is a test" class="test" id="33">testing</div>
----------
name div
*content ()
cls test
attrs {'content': ('testing', 'and testing'), 'id': 33, 'alt': 'this is a test', 'class': 'test'}
attrs_str  alt="this is a test" class="test" content="('testing', 'and testing')" id="33"
resp <div alt="this is a test" class="test" content="('testing', 'and testing')" id="33" />
----------

Значит, во втором случае '<div alt="this is a test" class="test" id="33" />' не должно быть. Я получаю <div alt="this is a test" class="test" content="('testing', 'and testing')" id="33" /> для той же самой функции.

РЕДАКТИРОВАТЬ 2: Вероятно, ваше пространство имен нарушено, поскольку имя **kwargs часто используется где-то еще, как вы используете здесь **attrs, поэтому измените имя этого модуля / прямая функция импорта должна решить вашу проблему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...