Proper way to use **kwargs in Python
You can pass a default value to get()
for keys that are not in the dictionary:
self.val2 = kwargs.get('val2',"default value")
However, if you plan on using a particular argument with a particular default value, why not use named arguments in the first place?
def __init__(self, val2="default value", **kwargs):
How do I use **kwargs in Python 3 class __init__ function?
General kwargs ideas
When you load variables with self.var = value
, it adds it to an internal dictionary that can be accessed with self.__dict__
.
class Foo1:
def __init__(self, **kwargs):
self.a = kwargs['a']
self.b = kwargs['b']
foo1 = Foo1(a=1, b=2)
print(foo1.a) # 1
print(foo1.b) # 2
print(foo1.__dict__) # {'a': 1, 'b': 2}
If you want to allow for arbitrary arguments, you can leverage the fact that kwargs
is also a dictionary and use the update()
function.
class Foo2:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
foo2 = Foo2(some_random_variable=1, whatever_the_user_supplies=2)
print(foo2.some_random_variable) # 1
print(foo2.whatever_the_user_supplies) # 2
print(foo2.__dict__) # {'some_random_variable': 1, 'whatever_the_user_supplies': 2}
This will prevent you from getting an error when you try to store a value that isn't there
class Foo3:
def __init__(self, **kwargs):
self.a = kwargs['a']
self.b = kwargs['b']
foo3 = Foo3(a=1) # KeyError: 'b'
If you wanted to ensure that variables a
or b
were set in the class regardless of what the user supplied, you could create class attributes or use kwargs.get()
class Foo4:
def __init__(self, **kwargs):
self.a = kwargs.get('a', None)
self.b = kwargs.get('b', None)
foo4 = Foo4(a=1)
print(foo4.a) # 1
print(foo4.b) # None
print(foo4.__dict__) # {'a': 1, 'b': None}
However, with this method, the variables belong to the class rather than the instance. This is why you see foo5.b
return a string, but it's not in foo5.__dict__
.
class Foo5:
a = 'Initial Value for A'
b = 'Initial Value for B'
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
foo5 = Foo5(a=1)
print(foo5.a) # 1
print(foo5.b) # Initial Value for B
print(foo5.__dict__) # {'a': 1}
If you are giving the users the freedom to specify any kwargs
they want, you can iterate through the __dict__
in a function.
class Foo6:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def do_something(self):
for k, v in self.__dict__.items():
print(f"{k} -> {v}")
foo6 = Foo6(some_random_variable=1, whatever_the_user_supplies=2)
foo6.do_something()
# some_random_variable -> 1
# whatever_the_user_supplies -> 2
However, depending on whatever else you have going on in your class, you might end up with a lot more instance attributes than the user supplied. Therefore, it might be good to have the user supply a dictionary as an argument.
class Foo7:
def __init__(self, user_vars):
self.user_vars = user_vars
def do_something(self):
for k, v in self.user_vars.items():
print(f"{k} -> {v}")
foo7 = Foo7({'some_random_variable': 1, 'whatever_the_user_supplies': 2})
foo7.do_something()
# some_random_variable -> 1
# whatever_the_user_supplies -> 2
Addressing your code
With your updated code, I would suggest using the self.__dict__.update(kwargs)
method. Then you can either raise an error when you don't encounter variable you're relying on (option1
method) or you can have a default value for the variable incase it's not defined (option2
method)
class MathematicalModel:
def __init__(self, var1, var2, var3, **kwargs):
self.var1 = var1
self.var2 = var2
self.var3 = var3
self.__dict__.update(kwargs) # Store all the extra variables
class MathematicalModelExtended(MathematicalModel):
def __init__(self, var1, var2, var3, **kwargs):
super().__init__(var1, var2, var3, **kwargs)
def option1(self):
# Trap error if you need var4 to be specified
if 'var4' not in self.__dict__:
raise ValueError("Please provide value for var4")
x = (self.var1 + self.var2 + self.var3) / self.var4
return x
def option2(self):
# Use .get() to provide a default value when the user does not provide it.
_var4 = self.__dict__.get('var4', 1)
x = (self.var1 + self.var2 + self.var3) / self.var4
return x
a = MathematicalModel(1, 2, 3)
b = MathematicalModelExtended(1, 2, 3, var4=4, var5=5, var6=6)
print(b.option1()) # 1.5
print(b.option2()) # 1.5
Granted, if MathematicalModel
will never use anything other than var1
, var2
, and var3
, there's no point in passing the kwargs
.
class MathematicalModel:
def __init__(self, var1, var2, var3, **kwargs):
self.var1 = var1
self.var2 = var2
self.var3 = var3
class MathematicalModelExtended(MathematicalModel):
def __init__(self, var1, var2, var3, **kwargs):
super().__init__(var1, var2, var3)
self.__dict__.update(kwargs)
def option1(self):
# Trap error if you need var4 to be specified
if 'var4' not in self.__dict__:
raise ValueError("Please provide value for var4")
x = (self.var1 + self.var2 + self.var3) / self.var4
return x
def option2(self):
# Use .get() to provide a default value when the user does not provide it.
_var4 = self.__dict__.get('var4', 1)
x = (self.var1 + self.var2 + self.var3) / self.var4
return x
a = MathematicalModel(1, 2, 3)
b = MathematicalModelExtended(1, 2, 3, var4=4, var5=5, var6=6)
print(b.option1()) # 1.5
print(b.option2()) # 1.5
Use kwargs in a function when it contains arguments not-used by the function
You can do it, just add kwargs to sub1 and it will auto-parse any named arguments you provided, and the rest will be put into kwargs:
def sub1(a, b, foo="bar", **kwargs):
That way you can still call sub1 with or without foo, but you can also call it with or without other arguments without causing an error.
main(1, 2, foo="hello")
> 1, 2, "hello"
main(1, 2, qux="hello")
> 1, 2, "bar"
What is the purpose and use of **kwargs?
You can use **kwargs
to let your functions take an arbitrary number of keyword arguments ("kwargs" means "keyword arguments"):
>>> def print_keyword_args(**kwargs):
... # kwargs is a dict of the keyword args passed to the function
... for key, value in kwargs.iteritems():
... print "%s = %s" % (key, value)
...
>>> print_keyword_args(first_name="John", last_name="Doe")
first_name = John
last_name = Doe
You can also use the **kwargs
syntax when calling functions by constructing a dictionary of keyword arguments and passing it to your function:
>>> kwargs = {'first_name': 'Bobby', 'last_name': 'Smith'}
>>> print_keyword_args(**kwargs)
first_name = Bobby
last_name = Smith
The Python Tutorial contains a good explanation of how it works, along with some nice examples.
Python 3 update
For Python 3, instead of iteritems()
, use items()
The right way to use **kwargs in Python
You have two separate questions with two separate pythonic ways of answering those questions.
1- Your first concern was that you don't want to keep adding new lines the more arguments you start supporting when formatting a string. The way to work around that is using a defaultdict
so you're able to return an empty string when you don't provide a specific keyword argument and str.format_map
that accepts a dict as a way to input keyword arguments to format. This way, you only have to update your string and what keyword arguments you want to print:
from collections import defaultdict
def call_me_by_name(**kwargs):
default_kwargs = defaultdict(str, kwargs)
print("Your name is {first_name} {second_name} {third_name}".format_map(default_kwargs))
2- If, on the other hand and answering your second question, you want to provide different behavior depending on the keyword arguments, like changing the way a string looks or providing different file lookup functionalities, without using if statements, you have to add different functions/methods and call them from this common function/method. Here are two ways of doing that:
OOP:
class FileLookup:
def parse(self, **kwargs):
return getattr(self, next(iter(kwargs)))(**kwargs)
def line_number(self, line_number):
print('parsing with a line number: {}'.format(line_number))
def byte_position(self, byte_position):
print('parsing with a byte position: {}'.format(byte_position))
fl = FileLookup()
fl.parse(byte_position=10)
fl.parse(line_number=10)
Module:
def line_number(line_number):
print('parsing with a line number: {}'.format(line_number))
def byte_position(byte_position):
print('parsing with a byte position: {}'.format(byte_position))
def parse(**kwargs):
return globals()[next(iter(kwargs))](**kwargs)
parse(byte_position=29)
parse(line_number=29)
Using **kwargs in a Python function
Call:
find_missing(save=True, path="/path/...")
Access the value using kwargs['save'] and kwargs['path']
def find_missing(self, **kwargs):
if kwargs['save'] == True:
tmp = find_one_missing(self.data)
tmp.to_csv(kwargs['path'])
else:
return find_one_missing(self.data)
How to use **kwargs to change a default value?
Use default value of dict.get
def foo(**kwargs):
key = kwargs.get("key", "default")
Use of *args and **kwargs
The syntax is the *
and **
. The names *args
and **kwargs
are only by convention but there's no hard requirement to use them.
You would use *args
when you're not sure how many arguments might be passed to your function, i.e. it allows you pass an arbitrary number of arguments to your function. For example:
>>> def print_everything(*args):
for count, thing in enumerate(args):
... print( '{0}. {1}'.format(count, thing))
...
>>> print_everything('apple', 'banana', 'cabbage')
0. apple
1. banana
2. cabbage
Similarly, **kwargs
allows you to handle named arguments that you have not defined in advance:
>>> def table_things(**kwargs):
... for name, value in kwargs.items():
... print( '{0} = {1}'.format(name, value))
...
>>> table_things(apple = 'fruit', cabbage = 'vegetable')
cabbage = vegetable
apple = fruit
You can use these along with named arguments too. The explicit arguments get values first and then everything else is passed to *args
and **kwargs
. The named arguments come first in the list. For example:
def table_things(titlestring, **kwargs)
You can also use both in the same function definition but *args
must occur before **kwargs
.
You can also use the *
and **
syntax when calling a function. For example:
>>> def print_three_things(a, b, c):
... print( 'a = {0}, b = {1}, c = {2}'.format(a,b,c))
...
>>> mylist = ['aardvark', 'baboon', 'cat']
>>> print_three_things(*mylist)
a = aardvark, b = baboon, c = cat
As you can see in this case it takes the list (or tuple) of items and unpacks it. By this it matches them to the arguments in the function. Of course, you could have a *
both in the function definition and in the function call.
Related Topics
Label Python Data Points on Plot
Save List of Dataframes to Multisheet Excel Spreadsheet
Python Socket Receive Large Amount of Data
How to Convert Integer Timestamp into a Datetime
How to Decode Base64 Data in Python
Matplotlib Scatter Plot Legend
Run Python Script Without Windows Console Appearing
Matplotlib: Save Plot to Numpy Array
Class Method Decorator with Self Arguments
"Cannot Open Include File: 'Config-Win.H': No Such File or Directory" While Installing MySQL-Python
Pandas - Plotting a Stacked Bar Chart
JSONify a SQLalchemy Result Set in Flask
How to Increase the Cell Width of the Jupyter/Ipython Notebook in My Browser
How to Index into a Dictionary
Function for Factorial in Python
Working with Big Data in Python and Numpy, Not Enough Ram, How to Save Partial Results on Disc