I have this following function defined in a file named kwargs.py. (changing the module to unpacking.py made the function work as expected). 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
. (Even if the tuple is not unpacked i was expecting at least the tuple itself be assigned to content[0]).
What am i missing here?
*content
is not an argument. 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. 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.
https://docs.python.org/3/tutorial/controlflow.html#arbitrary-argument-lists
This 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.
For this 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. I get <div alt="this is a test" class="test" content="('testing', 'and testing')" id="33" />
for the exactly same function.
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.
First let's make sure we are using the same terminology:
name, *content, cls=None, **attrs
in your example), '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.
First let's look at the function signature:
def tag(name, *content, cls=None, **attrs):
...
This function defines four parameters:
name
- a positional-or-keyword parameter, content
- a varargs-parameter which captures any number of additional positional arguments, cls
- a keyword-only parameter with a default value, attrs
- a keywords-parameter which captures any number of additional keyword arguments. When you call this function in the following way this is what happens:
tag('div', 'testing', cls='test', **dict(id=33, alt='this is a test'))
'div'
is bound to name
, 'testing'
is captured by content
resulting in a 1-tuple, 'test'
is bound to cls
, id=33, alt='this is a test'
are captured by 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. Ie you can't bind content=(1, 2)
. Instead if you pass tag('foo', 1, 2)
this binding happens automatically. So if you call the function in the following way:
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
. This is because *content
only captures positional arguments.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.