简体   繁体   English

python 使用 *args 和 **kwargs 以及其他仅位置和关键字参数解包 arg 列表

[英]python unpacking arg list with *args and **kwargs along with other positional and keyword only params

I have this following function defined in a file named kwargs.py.我在名为 kwargs.py 的文件中定义了以下 function。 (changing the module to unpacking.py made the function work as expected). (将模块更改为 unpacking.py 使 function 按预期工作)。 See the image at the bottom of the question.请参阅问题底部的图像。

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)

when executing this执行此操作时

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

The result i get is我得到的结果是

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

But when i execute this但是当我执行这个

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

i only get我只得到

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

why is the parameter name getting assigned but not content .为什么要分配参数name但未分配content (Even if the tuple is not unpacked i was expecting at least the tuple itself be assigned to content[0]). (即使元组没有解包,我也希望至少元组本身被分配给 content[0])。

What am i missing here?我在这里想念什么?

Edit: Python 3.8.3 x86 in Windows 10编辑: Windows 10 中的 Python 3.8.3 x86 在此处输入图像描述

*content is not an argument. *content不是论据。 You can't assign anything to it.你不能给它分配任何东西。

The docs indeed don't explain it explicitly.文档确实没有明确解释。 It's just a variable that you can use within your function body.它只是一个可以在 function 主体中使用的变量。 What it does is "it scoops up all remaining input arguments".它的作用是“收集所有剩余的输入参数”。

Normally, these variadic arguments will be last in the list of formal parameters, because they scoop up all remaining input arguments that are passed to the function.通常,这些可变参数 arguments 将在形式参数列表中排在最后,因为它们会收集传递给 function 的所有剩余输入 arguments。

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

This function本 function

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


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

will print将打印

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

EDIT:编辑:

Your examples are wrong.你的例子是错误的。 You can't get a different output.您无法获得不同的 output。

For this function对于这个 function

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

If you run this如果你运行这个

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'))

You get你得到

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" />
----------

So you shouldn't get '<div alt="this is a test" class="test" id="33" />' for the second case.所以你不应该在第二种情况下得到'<div alt="this is a test" class="test" id="33" />' I get <div alt="this is a test" class="test" content="('testing', 'and testing')" id="33" /> for the exactly same function.对于完全相同的 function,我得到<div alt="this is a test" class="test" content="('testing', 'and testing')" id="33" />

EDIT 2: Probably your namespace is broken as name **kwargs is often used somewhere else in a way you use **attrs here so changing this module name / importing function directly should solve your issue.编辑2:可能您的名称空间已损坏,因为名称**kwargs经常以您在此处使用**attrs的方式在其他地方使用,因此更改此模块名称/直接导入 function 应该可以解决您的问题。

First let's make sure we are using the same terminology:首先让我们确保我们使用相同的术语:

  • A function declares parameters ( name, *content, cls=None, **attrs in your example), A function 声明参数(在您的示例中name, *content, cls=None, **attrs ),
  • A function call receives arguments (eg 'div', 'testing', cls='test', **dict(id=33, alt='this is a test') ).一个 function 调用接收arguments (例如'div', 'testing', cls='test', **dict(id=33, alt='this is a test') )。

The arguments are then bound to the parameters, so they can be accessed in the function body.然后将 arguments 绑定到参数,因此可以在 function 主体中访问它们。

First let's look at the function signature:首先让我们看一下 function 签名:

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

This function defines four parameters:这个function定义了四个参数:

  • name - a positional-or-keyword parameter, name - 位置或关键字参数,
  • content - a varargs-parameter which captures any number of additional positional arguments, content - 一个可变参数参数,它捕获任意数量的附加位置arguments,
  • cls - a keyword-only parameter with a default value, cls - 具有默认值的仅限关键字的参数,
  • attrs - a keywords-parameter which captures any number of additional keyword arguments. attrs - 一个关键字参数,它捕获任意数量的附加关键字arguments。

When you call this function in the following way this is what happens:当您以下列方式调用此 function 时,会发生以下情况:

tag('div', 'testing', cls='test', **dict(id=33, alt='this is a test'))
  • 'div' is bound to name , 'div'绑定到name
  • 'testing' is captured by content resulting in a 1-tuple, 'testing'content捕获,产生一个 1 元组,
  • 'test' is bound to cls , 'test'绑定到cls
  • id=33, alt='this is a test' are captured by attrs . id=33, alt='this is a test'attrs捕获。

Now the special thing about the *content and **attrs parameters is that they capture any number of excess arguments but they can't be bound to directly.现在关于*content**attrs参数的特殊之处在于它们捕获任意数量的多余 arguments 但它们不能直接绑定。 Ie you can't bind content=(1, 2) .即你不能绑定content=(1, 2) Instead if you pass tag('foo', 1, 2) this binding happens automatically.相反,如果你传递tag('foo', 1, 2)这个绑定会自动发生。 So if you call the function in the following way:因此,如果您通过以下方式调用 function:

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

Then all arguments are provided by keyword and hence all except name and cls are captured by attrs .然后所有 arguments 都由关键字提供,因此除namecls之外的所有内容都由attrs捕获。 This is because *content only captures positional arguments.这是因为*content仅捕获位置arguments。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM