In the last lesson we saw how the asterix operator
could be used to provide any number of positional arguments to a function
because it collected them into a tuple. In this lesson,
we'll take a look at the double asterix operator,
and this is going to allow us to work with an arbitrary number of keyword
arguments. Let's see this in action.
So let's say I create another function called calculate,
and this time I'm going to add two asteriks. And in this case,
I'm going to call my argument **kwargs or keyword arguments.
What we've done now is we've added two asterisks signs in front of this parameter
name,
and we've now created unlimited keyword arguments.
What this allows me to do is I could basically now call this calculate function,
I could pass in a keyword argument, for example,
add, and let's give it a value, maybe three,
and then let's add another keyword argument which I'll call multiply,
set that to equal five. And effectively
what this has been turned into is these **kwargs,
which is basically a dictionary. So if I print these **kwargs,
you can see what gets printed is basically a bog-standard dictionary.
And in fact, if we do that type check thing again, so type,
you can see it is in fact, a dictionary.
This dictionary basically represents each of the keyword arguments and their
values.
So add is now a key and three is now it's value, multiply's
the second key and five is its value.
What I can do here is I could do the standard way of looping through dictionary.
So I could say for key, value in **kwargs .items,
and then I can get access to each of the keys and their values as I loop through
the dictionary like this.
So add three multiplied by five. Alternatively,
I can simply use the names of the keys.
So I could just simply print my **kwargs and then use the square brackets and then
provide the name of the key, so add for example.
And that will give me the value which is three as you can see here.
Now,
what this allows me to do is it basically lets me look through all of the inputs
and find the ones that I want and use them to do something.
So for example, in this case, it's a calculate function, right?
So we could let's say start off with a normal positional argument,
so let's call it n and let's pass in that n at the very beginning.
So let's say we start out with the number 2, n is now equal to 2,
and then the **kwargs is a dictionary of the remainder arguments.
Let's say that I wanted to do some calculations.
I wanted to say n+= kwargs,
and I'm going to get hold of the value under add.
And then I'm going to say, n*= the kwargs,
and I'm going to get hold of value of multiply.
So now when I go ahead and print
my n, you can see that what happens is it takes the first value 2 as equal to n,
and then n, this 2, is going to be added to the number I wanted to add
which is 3. So 2 + 3 is 5,
and then it multiplied that 5 by whatever it is I had stored in multiply.
So 5 plus 5, and we get 25.
This gives us a more flexible way of working with these arguments.
And it gives us a way to name the values that we're passing in to this function.
Now, coming back to what they've done in the Tkinter module,
basically this Tkinter module is actually ported from another technology called
TK.
And TK actually has a very different syntax from Python.
In order for it to work, they basically took all of the Tk commands,
like creating a label or packing some sort of item,
and took all of the options and turn them into these **kwargs or optional keyword
arguments.
And that is why when we create a new label from the Tkinter module
or when we call the pack method,
you can see that it doesn't actually come up with any properties that we can
modify other than this **kw,
which is the same as our **kwargs.
So we could actually create a class like that as well,
because this is done at initialization, right?
This is done when they're creating that label class.
Let's say that we create a class like this which I'll car,
and in the init,
what I'll include in addition to self is **
kw, or **kwargs, or however you want to spell it.
This kw are going to be all of the optional arguments that I'll pass in
when I'm initializing a new object from this class. I can say,
for example, self.
make of the car equals kw.
And then from kw, I'm going to get the make.
And then I'm going to have self.model
which is going to be equal to kw and then model. And of course,
all of the spelling matters so make sure you don't make any typos.
And then I decide to create my car
which is going to be the object created from this class. Now notice how
when I open up the parentheses here,
you don't actually see any of the properties like make and model show up
when I'm initializing. All you see is **
kw, just in the same way as you see it here,
**kw.
And that of course refers to our optional keyword arguments.
Let's add some values. Let's set the make to a Nissan,
and lets set the model to a-- let's set it to a GT-R. And now I can use my object
just as I would any other object. I can say my_car.model.
And when I hit run, you can see I've got GT-R being printed here.
And if I decided to print make, you can see
it gets hold off the make.
Now, what if I didn't actually specify one of these arguments.
Notice how it's now actually going to crash.
It tells me that keyError model.
So when it was initializing this car object, it fell on this line. And it
couldn't get hold of a value called model from this kw argument dictionary.
In fact, with dictionaries,
we can get hold of the values through the square bracket method,
but we can also use a function called get. And we use get in a very similar way
to the way that we use the square brackets.
We just pass in the name of the key and we want to get hold of the value.
But the benefit of get is that if this key doesn't exist in the dictionary,
then it will just return none and it won't give us an error.
So now if I run this code again, you can see that everything works,
but my car.model is now equal to none.
So this is how we can actually create a class with lots of optional keyword
arguments.
So we could have a self.color or self.seats.
And depending on what it is we want to initialize this car object to,
then we can simply go ahead and add whatever it is we want to set as the make
and the model or the color and the seats. And in the same way
that way using this label class,
we can set the text or we can set the font.
We can set all of the options or we can leave them as the default values. That
should help you understand how this code works a little bit better.
And the reason why it works like this is, as I say,
mostly because this module was converted from another language and this just
happened to be the most efficient way for them to do this. Now with other more
Pythonic modules or things that have been created in Python
like the turtle module, then you'll see more bog-standard kind of code
like what we've been working with. So when we initialize a new turtle,
you can see that it has a default shape,
it has a visible boolean,
and we can set all of those properties that we can see in this prompt.
Now in the next lesson,
I want to dive deeper into the TKinter documentation
and I want to show you how we can create other components, not just the label,
but things like buttons and text inputs
and a whole range of things that will bring our GUI program to life.
So for all of that and more, I'll see you in the next lesson.