What does this to in python super().__init__(**kwargs) - python

I know that super is used to call the init method in the superclass, I'm having trouble understanding what kwargs does I know it takes key arguments
what does it do here?
class LoginScreen(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.cols = 2
self.add_widget(Label(text = "Username: "))
self.username = TextInput(multiline = False)
self.add_widget(self.username)
self.add_widget(Label(text="Password: "))
self.username = TextInput(multiline=False, password=True)
self.add_widget(self.username)
self.add_widget(Label(text="Two Factor Auth: "))
self.tffa = TextInput(multiline=False, password=True)
self.add_widget(self.tffa)

def __init__(self, **kwargs): packs all of the keyword arguments used in any given call to __init__ into a dict
super().__init__(**kwargs): expands them into keyword arguments again.
It's like a wildcard for function parameters. It can be a lazy way to give the subclass the same parameter signature as the parent without bothering to type all of the possible keyword parameters in again.
Just grab them as a blob and shovel them off to the parent to figure out.

For example you put this :
x = LoginScreen(size=1, blahblah=2, something=3)
We have now:
>>> print(kwargs)
{'size': 1, 'blahblah': 2, 'something': 3}
So if it reaches the following line: super().__init__(**kwargs)
it will be equal to that : super().__init__(size=1, blahblah=2, something=3)
Becarefully: if you didn't put double asterisks "**" it will be equal to one argument as key:
`super().__init__({'size': 1, 'blahblah': 2, 'something': 3})`
And you can also use it like that:
options = [1,2,3]
def func(first=None, second=None, last=None):
print("first arg:", first)
print("second arg:", second)
print("third arg:", last)
func(*options)
Here we use one asterisk since this is a list not a dict so that means expand the options list for each argument, the output will be:
first arg: 1
second arg: 2
third arg: 3
But if we call options without the asterisk.
func(options)
See what's gonna happen:
first arg: [1, 2, 3]
second arg: None
third arg: None
The samething with kwargs except they go like that:
fuction(key1=value1, key2=value2, key3=value3, ...)

Related

Compact way to pass same value to arguments

When I have a function with bunch of optional arguments
def function_name(arg1=0, arg2=0, arg3=0, arg4=0, ...):
.....doing anything
and I want to pass same value to some of them
function_name(arg1=1, arg3=1, arg6=1)
Is there a more compact way to do it?
so I don't have to keep copying =value and filling up the line.
After several helpful replies I think I came to the answer I was looking for:
def myargs(value, *args):
kargs = {}
for arg in args:
kargs[arg] = value
return kargs
function_name(**myargs(1, 'arg1', 'arg3', 'arg6'))
Thanks guys, sorry if I was bad at explaining what I needed.
I think what you are wanting to do is keyword argument unpacking. You can pass a dictionary to the function and it will unpack the arguments you have included. Anything not included uses the default values.
def test(w=0, x=0, y=0, z=0):
return w+x+y+z
kwargs_1 = {'y': 1}
kwargs_2 = {'x': 2, 'z': 3}
test(**kwargs_1)
# returns:
1
test(**kwargs_2)
# returns:
5
You can use default arguments:
def func(arg1, arg2, def_arg1="1", def_arg2="2"):
pass
You can just use **kwargs
def function_name(arg, argOpt1=0, argOpt2=0, **kwargs):
pass
kwargs = {"argOpt1" : 123, "argOpt2" : 234}
function_name('argument1', kwargs)
Look at this
and this
If the arguments that are to have the same value are contiguous, then:
def function_name(arg1=0,arg2=0,arg3=0,arg4=0):
print(arg1, arg2, arg3, arg4)
function_name(2, *[1] * 3)
Prints:
2 1 1 1
If you didn't write that function, and you can't change its signature, you can pass the right number of arguments that way :
function_name(*(0,) * function_name.__code__.co_argcount)
It will pass the same value (in that case 0) to all the arguments the function expects.
If you need to pass only 10 arguments for instance, you can write :
function_name(*(0,) * 10)
If you want to target specific arguments by their position and not their names, you can use this :
import inspect
def func(a, b, c=None, d='foo', e=False):
print(a, b, c, d, e)
def fill_args(fun, value, arg_numbers):
return {
name: value
for name in (
tuple(inspect.signature(fun).parameters)[i]
for i in arg_numbers
)
}
if __name__ == '__main__':
func(**fill_args(func, 0, (0, 1, 2)))
func(**fill_args(func, 'bar', (0, 1, 4)))
func(**fill_args(func, True, (0, 1, 3)))

Optional Parameters in **kwargs

I am not able to handle to pass optional parameters in python **kwargs
def ExecuteyourQuery(self, queryStatement, *args, **kwargs):
if self.cursorOBJ is not None:
resultOBJ = self.cursorOBJ.execute(queryStatement, *args,**kwargs)
self.resultsVal = resultOBJ.fetchall()
The below statement works fine when I am calling the function
ExecuteyourQuery('select * from item_table where x = :x and y:y', x, y)
But when I give an extra argument which is not mentioned in the query, like
ExecuteyourQuery('select * from item_table where x = :x and y:y', x, y, z)
It returns 0 as output
When using args as the last parameter of your function, you can pass any number of arguments after the formal arguments, when existing. Args is a tuple.
def my_method(farg, *args):
print('FARG: ', farg)
print('ARGS: ', args)
my_method('Formal Argument', 1, 2, 3)
When using kwargs as the last parameter of your function, you can pass any number of named arguments after the formal arguments, when existing. Kwargs is a dictionary.
def my_method(farg, **kwargs):
print('FARG: ', farg)
print('KWARGS: ', kwargs)
my_method('Formal Argument', kwarg_1 = 1, kwarg_2 = 2, kwarg_3 = 3)
When using both args and kwargs as the last parameteres of your function, you can pass any number of arguments (that will be mapped to args) and named arguments (that will be mapped to kwargs) after the formal arguments, when existing.
def my_method(farg, *args, **kwargs):
print('FARG: ', farg)
print('ARGS: ', args)
print('KWARGS: ', kwargs)
my_method('Formal Argument', 1, 2, 3, kwarg_1 = 1, kwarg_2 = 2, kwarg_3 = 3)
Try this out and see the results in your console, hopefully it will help you solve your original problem :)
another way to do this is by setting a default value for a parameter
def method(one, two, three=3):
print(one)
print(two)
if three != 3: # don't have to use it like this but it is a default value
print(three)
this set a default value if the parameter is not filled
if it is filled it will overrule the default value and use that
method(11, 22) # prints first two parameters
method(23, 54, 89) # prints all three
Not sure if this is what your looking for but it is another way

Why does my function need an argument and how to call it with+without?

I have a marvellous function here:
def update_config(val):
config = configparser.ConfigParser()
fonts_comb = ""
for i in range(len(fonts)):
if i == len(fonts) - 1:
fonts_comb = fonts_comb + fonts[i]
else:
fonts_comb = fonts_comb + fonts[i] + ", "
config["LISTS"] = {"Fonts": fonts_comb}
config["variables"] = {"font_size": (screen_font_size.var).get(),
"x_offset": (x_offset_spin.var).get(),
"y_offset": (y_offset_spin.var).get(),
"language": language,
"preview_font_size": (preview_font_size_spin.var).get()}
variables = config["variables"]
if (fonts_menu.var).get() != strings[17]:
variables["font"] = (fonts_menu.var).get()
else:
variables["font"] = font
if (preview_fonts.var).get() != strings[18]:
variables["preview_font"] = (preview_fonts.var).get()
else:
variables["preview_font"] = preview_font
with open("config.ini", "w") as configfile:
config.write(configfile)
I don't know if it's relevant, too, but basically it does what the name says - updates the config file.
What I don't like about the function is that it requires an argument (val here, should be self maybe?). And 'cause it requires that argument, I can't call it "properly". Let me demonstrate, the following works just fine:
class OptionMenus(tk.OptionMenu):
def __init__(self, master, status, *fonts):
self.var = tk.StringVar(master)
(self.var).set(status)
(tk.OptionMenu).__init__(self, master, self.var, *fonts,
command = update_config)
However - calling like the following returns this error: TypeError: update_config() takes 0 positional arguments but 1 was given
class Spinboxes(tk.Spinbox):
def __init__(self, master):
self.var = tk.IntVar()
tk.Spinbox.__init__(self, master, textvariable = self.var,
from_ = 0, to = 100, command = update_config)
For now, I have solved it using this:
def crossover():
val = ""
update_config(val)
But it seems to be kind of a monkey-ish way to do things, so is there a better way to call that function?
Use a default argument value:
def update_config(val=None):
# etc.
You could also remove the argument entirely and use a single-argument lambda to call it in a context where that the argument must be passed:
def update_config():
# etc.
# ...
tk.OptionMenu.__init__(self, master, self.var, *fonts,
command=lambda _: update_config())
But I think the first option is simpler.
update_config looks like an instance method, so yes, I recommend using the accepted variable self.
If an error says it takes 0 arguments but 1 was given, that means exactly what it says. This means that calling update_config from the Spinbox object passes it an argument. However, since it works fine from OptionMenus and therefore works without an argument, you need to set it up to handle an optional argument.
Change:
def update_config(val):
to:
def update_config(self, event=None):
and that should fix the issue.

Separating **kwargs for different functions

Given a higher order function that takes multiple functions as arguments, how could that function pass key word arguments to the function arguments?
example
def eat(food='eggs', how_much=1):
print(food * how_much)
def parrot_is(state='dead'):
print("This parrot is %s." % state)
def skit(*lines, **kwargs):
for line in lines:
line(**kwargs)
skit(eat, parrot_is) # eggs \n This parrot is dead.
skit(eat, parrot_is, food='spam', how_much=50, state='an ex-parrot') # error
state is not a keyword arg of eat so how can skit only pass keyword args relevant the function that it is calling?
You can filter the kwargs dictionary based on func_code.co_varnames (in python 2) of a function:
def skit(*lines, **kwargs):
for line in lines:
line(**{key: value for key, value in kwargs.iteritems()
if key in line.func_code.co_varnames})
In python 3, __code__ should be used instead of func_code. So the function will be:
def skit(*lines, **kwargs):
for line in lines:
line(**{key: value for key, value in kwargs.iteritems()
if key in line.__code__.co_varnames})
Also see: Can you list the keyword arguments a function receives?
If you add **kwargs to all of the definitions, you can pass the whole lot:
def eat(food='eggs', how_much=1, **kwargs):
print(food * how_much)
def parrot_is(state='dead', **kwargs):
print("This parrot is %s." % state)
def skit(*lines, **kwargs):
for line in lines:
line(**kwargs)
Anything in **kwargs that isn't also an explicit keyword argument will just get left in kwargs and ignored by e.g. eat.
Example:
>>> skit(eat, parrot_is, food='spam', how_much=50, state='an ex-parrot')
spamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspamspam
This parrot is an ex-parrot.
def sample(a,b,*,c=0):
print(a,b,c)
* -> is before argument is args and after argument is kwargs(keyword arguments)
sample(1,2,c=10)
sample(a=1,b=2,c=1) # this also work
sample(1,2,c) # TypeError: sample() takes 2 positional arguments but 3 were given

argparse argument order

I have a little problem.
I use argparse to parse my arguments, and it's working very well.
To have the args, I do :
p_args = parser.parse_args(argv)
args = dict(p_args._get_kwargs())
But the problem with p_args is that I don't know how to get these arguments ordered by their position in the command line, because it's a dict.
So is there any possibility to have the arguments in a tuple/list/ordered dict by their order in the command line?
To keep arguments ordered, I use a custom action like this:
import argparse
class CustomAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
if not 'ordered_args' in namespace:
setattr(namespace, 'ordered_args', [])
previous = namespace.ordered_args
previous.append((self.dest, values))
setattr(namespace, 'ordered_args', previous)
parser = argparse.ArgumentParser()
parser.add_argument('--test1', action=CustomAction)
parser.add_argument('--test2', action=CustomAction)
To use it, for example:
>>> parser.parse_args(['--test2', '2', '--test1', '1'])
Namespace(ordered_args=[('test2', '2'), ('test1', '1')], test1=None, test2=None)
If you need to know the order in which the arguments appear in your parser, you can set up the parser like this:
import argparse
parser = argparse.ArgumentParser(description = "A cool application.")
parser.add_argument('--optional1')
parser.add_argument('positionals', nargs='+')
parser.add_argument('--optional2')
args = parser.parse_args()
print args.positionals
Here's a quick example of running this code:
$ python s.py --optional1 X --optional2 Y 1 2 3 4 5
['1', '2', '3', '4', '5']
Note that args.positionals is a list with the positional arguments in order. See the argparse documentation for more information.
This is a bit fragile since it relies on understanding the internals of argparse.ArgumentParser, but in lieu of rewriting argparse.ArgumentParser.parse_known_args, here's what I use:
class OrderedNamespace(argparse.Namespace):
def __init__(self, **kwargs):
self.__dict__["_arg_order"] = []
self.__dict__["_arg_order_first_time_through"] = True
argparse.Namespace.__init__(self, **kwargs)
def __setattr__(self, name, value):
#print("Setting %s -> %s" % (name, value))
self.__dict__[name] = value
if name in self._arg_order and hasattr(self, "_arg_order_first_time_through"):
self.__dict__["_arg_order"] = []
delattr(self, "_arg_order_first_time_through")
self.__dict__["_arg_order"].append(name)
def _finalize(self):
if hasattr(self, "_arg_order_first_time_through"):
self.__dict__["_arg_order"] = []
delattr(self, "_arg_order_first_time_through")
def _latest_of(self, k1, k2):
try:
print self._arg_order
if self._arg_order.index(k1) > self._arg_order.index(k2):
return k1
except ValueError:
if k1 in self._arg_order:
return k1
return k2
This works through the knowledge that argparse.ArgumentParser.parse_known_args runs through the entire option list once setting default values for each argument. Meaning that user specified arguments begin the first time __setattr__ hits an argument that it's seen before.
Usage:
options, extra_args = parser.parse_known_args(sys.argv, namespace=OrderedNamespace())
You can check options._arg_order for the order of user specified command line args, or use options._latest_of("arg1", "arg2") to see which of --arg1 or --arg2 was specified later on the command line (which, for my purposes was what I needed: seeing which of two options would be the overriding one).
UPDATE: had to add _finalize method to handle pathological case of sys.argv() not containing any arguments in the list)
There is module especially made to handle this :
https://github.com/claylabs/ordered-keyword-args
without using orderedkwargs module
def multiple_kwarguments(first , **lotsofothers):
print first
for i,other in lotsofothers:
print other
return True
multiple_kwarguments("first", second="second", third="third" ,fourth="fourth" ,fifth="fifth")
output:
first
second
fifth
fourth
third
On using orderedkwargs module
from orderedkwargs import ordered kwargs
#orderedkwargs
def mutliple_kwarguments(first , *lotsofothers):
print first
for i, other in lotsofothers:
print other
return True
mutliple_kwarguments("first", second="second", third="third" ,fourth="fourth" ,fifth="fifth")
Output:
first
second
third
fourth
fifth
Note: Single asterik is required while using this module with decorator above the function.
I needed this because, for logging purposes, I liked to print the arguments after they were parsed. The problem was that the arguments are not printed in order, which was really annoying.
The custom action class just flat out did not work for me. I had other arguments which used a different action such as 'store_true' and default arguments also don't work since the custom action class is not called if the argument is not given in the command line. What worked for me was creating a wrapper class like this:
import collections
from argparse import ArgumentParser
class SortedArgumentParser():
def __init__(self, *args, **kwargs):
self.ap = ArgumentParser(*args, **kwargs)
self.args_dict = collections.OrderedDict()
def add_argument(self, *args, **kwargs):
self.ap.add_argument(*args, **kwargs)
# Also store dest kwarg
self.args_dict[kwargs['dest']] = None
def parse_args(self):
# Returns a sorted dictionary
unsorted_dict = self.ap.parse_args().__dict__
for unsorted_entry in unsorted_dict:
self.args_dict[unsorted_entry] = unsorted_dict[unsorted_entry]
return self.args_dict
The pros are that the add_argument method should have the exact same functionality as the original ArgumentParser. The cons are that if you want other methods you will have to write wrapped for all of them. Luckily for me all I ever used was add_argument and parse_args, so this served my purposes pretty well. You would also need to do more work if you wanted to use parent ArgumentParsers.
This is my simple solution based on the existing ones:
class OrderedNamespace(argparse.Namespace):
def __init__(self, **kwargs):
self.__dict__["_order"] = [None]
super().__init__(**kwargs)
def __setattr__(self, attr, value):
super().__setattr__(attr, value)
if attr in self._order:
self.__dict__["_order"].clear()
self.__dict__["_order"].append(attr)
def ordered(self):
if self._order and self._order[0] is None:
self._order.clear()
return ((attr, getattr(self, attr)) for attr in self._order)
parser = argparse.ArgumentParser()
parser.add_argument('--test1', default=1)
parser.add_argument('--test2')
parser.add_argument('-s', '--slong', action='store_false')
parser.add_argument('--test3', default=3)
args = parser.parse_args(['--test2', '2', '--test1', '1', '-s'], namespace=OrderedNamespace())
print(args)
print(args.test1)
for a, v in args.ordered():
print(a, v)
Output:
OrderedNamespace(_order=['test2', 'test1', 'slong'], slong=False, test1='1', test2='2', test3=3)
1
test2 2
test1 1
slong False
It allows actions in add_argument(), which is harder for customized action class solution.

Categories

Resources