1

I use API with long name of argument parameters. Consequently, I create following dictionaries for most common combinations of values which are then unpacked in function calls.

a_T = {'API parameter a': True}
a_F = {'API parameter a': False} 
b_100 = {'API parameter b': 100}
b_0 = {'API parameter b': 0}
hello = {'API parameter c': 'hello'}
bye = {'API parameter d': 'goodbye'}    

myf(**a_T, **bye)
myf(**b_0)
myf(**a_F, **b_100, **hello, **bye)

Is there any way to avoid repeat double asterisk? The code becomes quite unreadable with many of these strange characters.

Once could then add this unpacking utility to myf:

myf(a_T, bye)
myf(b_0)
myf(a_F, b_100, hello, bye)
aeiou
  • 337
  • 1
  • 7
  • 1
    What are you gaining from `a = {'a': True}` compared to `a = True`? – matszwecja Jul 11 '22 at 11:41
  • @matszwecja Only some parameters are boolean, hence consistency. – aeiou Jul 11 '22 at 11:42
  • How is `a = {'a': True}; b = {'b': 100}` more consistent than `a = True; b = 100`? – matszwecja Jul 11 '22 at 11:43
  • @matszwecja You are asking good questions. The issue is a can also be False or b can be smth else than 100. – aeiou Jul 11 '22 at 11:45
  • 1
    I don't see how that is an issue – matszwecja Jul 11 '22 at 11:47
  • @Ch3steR After the edit it makes tiny bit more sense. – matszwecja Jul 11 '22 at 11:48
  • @matszwecja Yes. My guess is they are getting all these arguments from some API maybe? – Ch3steR Jul 11 '22 at 11:49
  • @Ch3steR good guess, there is indeed an API with clunky syntax. – aeiou Jul 11 '22 at 11:51
  • If you're going to ask about aesthetics, please provide *way* more background for a specific answer – Mad Physicist Jul 11 '22 at 11:51
  • 2
    The whole idea of having 26 separately named dicts stinks to high heaven in the first place, so it seems a bit out of place to ask about a couple of extra characters – Mad Physicist Jul 11 '22 at 11:53
  • @MadPhysicist added more background. Apologies. – aeiou Jul 11 '22 at 11:56
  • How do you determine which options to select? Surely you're mapping user input to this mess somehow? – Mad Physicist Jul 11 '22 at 12:02
  • @MadPhysicist The dictionary names are self-explanatory. Most API arguments are strings, I use arguments like `**west` or `**africa` instead of `side = 'west'` or `continent = 'Africa'`. – aeiou Jul 11 '22 at 12:08
  • 1
    How do you decide whether to select `a_T` or `a_F`? Sounds like you just added an additional, possibly unnecessary or inefficient, layer of indirection there – Mad Physicist Jul 11 '22 at 12:10
  • 1
    @aeiou If that really is something as simple as `continent = 'Africa'` then I would say that obfuscating your code with things like `**africa` makes the code much less clean, harder to read and more error-prone. – matszwecja Jul 11 '22 at 12:12
  • @MadPhysicist interesting point. There is around 10 arguments I use 100 times. The API argument name is very long, hence intention to shorten it. The shortened version is self-explanatory, no one will need to memorize the 10 assignments to understand what's going on. – aeiou Jul 11 '22 at 12:13
  • @matszwecja Good point about obfuscation, one will need to understand these arguments are unpacked somewhere... – aeiou Jul 11 '22 at 12:15

2 Answers2

4

You can actually use | for Python 3.9+ to combine all the dictionaries then send unpacked version.

def fun(**kwargs):
    print(kwargs)

>>> fun(**a_F| b_100| hello| bye)
{'API parameter a': False, 'API parameter b': 100, 'API parameter c': 'hello', 'API parameter d': 'goodbye'}

Or just use *args and pass multiple dictionaries:

def fun(*args):
    print(args)
    
>>> fun(a_F,b_100,hello,bye)
({'API parameter a': False}, {'API parameter b': 100}, {'API parameter c': 'hello'}, {'API parameter d': 'goodbye'})

Another solution is to use Python decorator and take care of ugly part inside the decorator function:

def decorator(fun):
    def wrapper(*args):
        from functools import reduce
        kwargs = reduce(lambda a, b: a | b, args)
        return fun(**kwargs)
    return wrapper
@decorator
def main_fun(**kwargs):
    print(kwargs)
    

>>> main_fun(a_F,b_100,hello,z)
{'API parameter a': False, 'API parameter b': 100, 'API parameter c': 'hello', 'parameter 4': 'goodbye'}
ThePyGuy
  • 17,779
  • 5
  • 18
  • 45
1

To unpack a series of dicts, use dict.update, or a nested comprehension:

def myf(*dicts):
    merged = {k: v for d in dicts for k, v in d.items()}
    # do stuff to merged

OR

def myf(*dicts):
    merged = {}
    for d in dicts:
        merged.update(d)
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264