Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Explaining get() method with **kwargs?

Trying to figure out how this code is working.

I understand that **kwargs returns a dictionary and the get() function searches the dict for a given key and returns a default value if not found. However in the code I don't understand is if the get() method is searching for example: "clock" or self.clock or both.

def update(self, *args, **kwargs):
    self.screen = kwargs.get("screen",self.screen)
    self.clock = kwargs.get("clock",self.clock)
    self.active = kwargs.get("active",self.active)

Here is an example call to this method:

debug.update(active = numActive)

From my understanding, the variable numActive is passed through the update method as active and then as **kwargs which is then searched for via the get() method. Couldn't I just remove the use of kwargs seeing as I know how many parameters are needed?

Any help with understanding is appreciated.

like image 461
Tazz B Avatar asked Apr 22 '18 02:04

Tazz B


4 Answers

The second parameter in the get method is the default value.

According to Python2.7's documentation:

get(key[, default])

Return the value for key if key is in the dictionary, else default. If default is not given, it defaults to None, so that this method never raises a KeyError.

Source: https://docs.python.org/2/library/stdtypes.html#dict.get

Explanation When you try to 'get' something from a dictionary, if the key is not found, or the value is None, it will return None.

BUT, if you provide the second parameter (which is the default), if and only if the key is not found, default value will be returned.

Example without defaults

For the following code:

some_dict = {
    "key_1": 1,
    "key_2": None,
}
print some_dict.get("key_1")
print some_dict.get("key_2")
print some_dict.get("key_3")

You will get the output:

1
None
None

Example with defaults

For the following code

some_dict = {
    "key_1": 1,
    "key_2": None,
}
print some_dict.get("key_1", 1)
print some_dict.get("key_2", 2)
print some_dict.get("key_3", 3)

You will get the output:

1
None
3

Looking at your code

I will explain your code in the comments:

def update(self, *args, **kwargs):
    # If the kwargs contain the key 'screen', the following get method will
    # return its value, or else it would remain whatever value was in 
    # self.screen's variable before
    self.screen = kwargs.get("screen",self.screen)


    # If the kwargs contain the key 'clock', the following get method will
    # return its value, or else it would remain whatever value was in 
    # self.clock's variable before
    self.clock = kwargs.get("clock",self.clock)

    # If the kwargs contain the key 'active', the following get method will
    # return its value, or else it would remain whatever value was in 
    # self.active's variable before
    self.active = kwargs.get("active",self.active)

Hope this helps. Cheers.

like image 80
Danny Avatar answered Oct 18 '22 20:10

Danny


This expression:

kwargs.get("screen",self.screen)

is equivalent to this expression:

kwargs["screen"] if "screen" in kwargs else self.screen

This statement:

self.screen = kwargs.get("screen",self.screen)

is equivalent to these statements:

if "screen" in kwargs:
    self.screen = kwargs["screen"]
else:
    self.screen = self.screen

Note that it is not equivalent to these statements:

if "screen" in kwargs:
    self.screen = kwargs["screen"]

Because the statement

self.screen = self.screen

raises an AttributeError if self.screen is not yet defined. See that error in action by running the following program:

d = {}
x = object()
x.attr = d.get("attr", x.attr)

You can do away with kwargs by implementing update instead as:

def update(self, screen=None, clock=None, active=None):
    if screen is not None: self.screen = screen
    if clock is not None: self.clock = clock
    if active is not None: self.active = active

And then you can call debug.update and specify whichever of the keyword arguments you want. Defining it this way has the advantage that it raises a TypeError if you try to call it with a keyword argument other than screen, clock, or active.

One caveat is that if you really mean to say debug.update(screen = None), then calling it that way actually does nothing; it does not set debug.screen to None. One way to get around that is to write this at the top of your file:

_UNSPECIFIED = object()

And then implement update as:

def update(self, screen=_UNSPECIFIED, clock=_UNSPECIFIED, active=_UNSPECIFIED):
    if screen is not _UNSPECIFIED: self.screen = screen
    if clock is not _UNSPECIFIED: self.clock = clock
    if active is not _UNSPECIFIED: self.active = active
like image 37
Jordan Avatar answered Oct 18 '22 18:10

Jordan


kwargs.get() retrieves a keyword argument passed to a function.

The first argument is the keyword and the second is the default value if there was no argument provided for the keyword.

from uuid import uuid4

def my_function(**kwargs):
    id = kwargs.get("id", uuid4().hex[:6])
    name = kwargs.get("name", None)
    print(f"{id} - {name}")

my_function(name="Alice")

a1b2c3 - Alice

my_function(name="Bob", id="d4e5f6")

d4e5f6 - Bob

my_function()

g7h8i9 - None

like image 36
kibaffo33 Avatar answered Oct 18 '22 18:10

kibaffo33


self.screen = kwargs.get("screen",self.screen)

This gets the "screen" value from kwargs. If its not there, it just uses whatever happens to be in screen at the moment and stuffs it back into screen. Its the same as

if "screen" in kwargs:
    self.screen = kwargs["screen"]

In other words, update only changes values that are passed as keyword arguments. This seems like a complicated way to do a simple thing. There must be a use case for it, but, generally, yuck.

Couldn't I just remove the use of kwargs seeing as I know how many parameters are needed?

In the example, no. It will set zero, onem two or three attributes depending on whether they are in kwarg. You could even put other values like update(foo="bar") and it would just ignore them.

So, here are two ways to set the attribute:

debug.update(active = numActive)
debug.active = numActive

I'm leaning towards the second way. Leaning way over!

like image 44
tdelaney Avatar answered Oct 18 '22 20:10

tdelaney