0

As I'm reading the Django documentation, I see this in the example:

from django.db import models

class HandField(models.Field):

    description = "A hand of cards (bridge style)"

    def __init__(self, *args, **kwargs):
        kwargs['max_length'] = 104
        super(HandField, self).__init__(*args, **kwargs)

I dont understand why the call to super is:

super(HandField, self).__init__(*args, **kwargs)

whereas (coming from C programming) I thought it should be:

super(HandField, self).__init__(args, kwargs)

How comes?

Yes. And I'm not asking what mean "*" and "**" (link marked as a duplicate), I'm asking why it's not re-sent without the star = why it's re-sent to parent with the stars, which means to me: "dictionary of a dictionary". And my question is different from the duplicate link, and the duplicate link doesnt answer to my question.

Olivier Pons
  • 15,363
  • 26
  • 117
  • 213
  • 4
    Are you thinking that the `*` here idicates a pointer? – kylieCatt Jan 27 '16 at 17:19
  • Yes. And I'm not asking what mean "`*`" and "`**`" (link marked as a duplicate), I'm asking **why** it's not re-sent **without** the star = why it's re-sent to parent **with the stars**, which means to me: "dictionary of a dictionary". And my question is different from the duplicate link, and the duplicate link doesnt answer to my question. – Olivier Pons Jan 27 '16 at 17:25
  • 1
    @OlivierPons: Why would you think it means "dictionary of a dictionary"? The answers in the duplicate explain exactly what calling a function with `*` and `**` arguments does, and it doesn't do what you're saying. – user2357112 Jan 27 '16 at 17:29
  • @OlivierPons see the bottom of the first answer: `Another usage...`. If that doesn't actually answer your question, please try to rephrase your question. – Wayne Werner Jan 27 '16 at 17:31
  • 3
    You call `foo(*args, **kwargs)` and not `foo(args, kwargs)` because `foo` accepts _some positional and keyword args_ and __not__ _tuple and dict_, because `args` is _just_ tuple and `kwargs` is _just_ dict – Lol4t0 Jan 27 '16 at 17:34
  • 1
    In a function definition they map many parameters to single variables. Positional parameters to a tuple, named parameters to a dictionary. In a function call, they go backwards, they expand single variables out to multiple parameters. A tuple -> positional parameters, a dictionary -> named parameters. – TessellatingHeckler Jan 27 '16 at 17:46
  • 1
    @OlivierPons: The duplicate does answer your question if you read a little further. The accepted answer first covers receiving `*` and `**`, then near the end covers _passing_ using `*` and `**`. – ShadowRanger Jan 27 '16 at 18:29
  • If you come from C then you should think of `(*args,**kwargs)` as kind of a varags function. The `*args` means that `args` will a tuple holding every positiional argument and `**kwargs` means `kwargs` will be a dictionary with all keyword arguments. That's why when you call `super` you don't use the `*` and `**`. Hope that helps – yorodm Jan 27 '16 at 18:43

1 Answers1

1

The reason you use */** is to unpack the packed values you were given. When your class is initialized, it might be initialized with:

HandField(1, 2, 3, abc=4, xyz=5)

Because it receives the arguments using variable length positional (*) and dynamic keyword (**) arguments (to avoid needing to remember and deal with the specifics of what its parent class constructor receives), args is received as (1, 2, 3) and kwargs is {'abc': 4, 'xyz': 5}. If the parent class is defined with a __init__ of:

def __init__(self, a1, a2, a3, spam=6, eggs=7, abc=None, xyz=None):

then calling super(HandField, self).__init__(args, kwargs) would pass the args tuple as a1, the kwargs dict as a2, and pass nothing at all for a3 or other arguments. By unpacking args and kwargs, you convert back to individual positional and keyword arguments, so a1 would get 1, a2 2, a3 3, abc 4 and xyz 5.

Basically, they're inverse operations; if you accept *, then the caller passes positional arguments one by one and they are "packed" into a single tuple, accepting ** accepts individual keyword arguments "packed" as a single dict. If you want to pass them along the same way, you "unpack" them to make them individual arguments, not collections of arguments.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271