James Franco James Franco - 2 months ago 9
Python Question

Template View - kwargs and **kwargs

I am reading about Template views through a tutorial and some of the code kind of confused me. The author used this code sample

from django.utils.timezone import now

class AboutUsView(TemplateView):
template_name = 'about_us.html'

def get_context_data(self, **kwargs):
context = super(AboutUsView, self).get_context_data(**kwargs)
if now().weekday() < 5 and 8 < now().hour < 18:
context['open'] = True
else:
context['open'] = False
return context


The thing that confused me syntactically was this statement

context = super(AboutUsView, self).get_context_data(**kwargs)


if we already are receiving
**kwargs
then why are we passing it to the super function with ** (double start). I think we should pass it as

context = super(AboutUsView, self).get_context_data(kwargs)


this is the contextMixin which is receiving this call.

class ContextMixin(object):
"""
A default context mixin that passes the keyword arguments received by
get_context_data as the template context.
"""

def get_context_data(self, **kwargs):
if 'view' not in kwargs:
kwargs['view'] = self
return kwargs


From what I have read is that the use of
**kwargs
pretty much means that kwargs is currently a dictionary and needs to be converted to named-value. If that is correct then how can kwargs be a dictionary when its parameter is actually **kwargs. I hope my question makes sense. Please let me know if you would want me to rephrase this.

Answer

In a function declaration, **kwargs will take all unspecified keyword arguments and convert them into a dictionary.

>>> test_dict = {'a':1, 'b':2}
>>> def test(**kwargs):
...     print (kwargs)
...
>>> test(**test_dict)
{'b': 2, 'a': 1}

Note that the dictionary object has to be converted using ** when it is passed to the function (test(**test_dict)) and when it is received by the function. It is impossible to do the following:

>>> test(test_dict)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: test() takes 0 positional arguments but 1 was given

So, in your example, the first **kwargs unpacks the keyword arguments into a dictionary, and then the second packs them back up to be sent to the parent.

A function with **kwargs in the signature can either received an unpacked dictionary or unspecified keyword arguments. Here's an example of the second case:

>>> def test(arg1, **kwargs):
...     print (kwargs)
...
>>> test('first', a=1, b=2)
{'b': 2, 'a': 1}