0

I am working on some software that queries a database. In particular, if you query the database with a certain parameter, it won't find a matching value, so you should query it again with a different parameter.

Below is a short script that outlines the problem. query_db is just a dummy function intended to mimic the behaviour of a query to a database containing an entry indexed by 1. In the get_db_params function, I query the db using the a parameter. If it returns None, then I try again with the b parameter. If that also returns None, then get_db_params throws an error.

Calling get_db_params with d1 and d2 as arguments returns "some_value", while d3 raises a KeyError.

My question is: this does not seem very pythonic, especially having two if params is None: in a row. Any advice on how to improve these functions?

def query_db(x):
    if x == 1:
        return "some_value"
    else:
        return None

def get_params(d):
    params = query_db(d['a'])
    if params is None:
        params = query_db(d['b'])
    if params is None:
        raise KeyError("Params not found in db")
    return params

d1 = {'a': 1, 'b': 1}
d2 = {'a': 0, 'b': 1}
d3 = {'a': 0, 'b': 0}

params = get_params(d1)
params = get_params(d2)
params = get_params(d3)
turnerm
  • 1,381
  • 11
  • 14
  • `params = query_db(d['a']) or query_db(d['b'])` – Chris_Rands Jul 31 '18 at 21:43
  • @Chris_Rands: that assumes that zero or an empty string (or anything that resolves to `False`) is logically the same as `None`. It might be, but you can't tell from the question. – cdarke Jul 31 '18 at 21:45
  • 1
    I was hoping to avoid using `or` due to the reasons discussed [here](https://stackoverflow.com/a/13710667/1812739). – turnerm Jul 31 '18 at 21:46
  • @cdarke yep agreed – Chris_Rands Jul 31 '18 at 21:48
  • Wouldn't it be better to make a single trip to the DB, get both results and use whichever is available? – mVChr Jul 31 '18 at 21:57
  • Why are you raising an error? the line `params = query_db(d['b'])` is useless because nothing will be returned after raising an error. Could you clarify what you want get_params() to actually accomplish? – Triggernometry Jul 31 '18 at 22:00
  • 1
    What you actually want here is `params = query_db(d['a']) ?? query_db(d['b'])`, with a null-coalescing `??` instead of a falsey-coalescing `or`, as proposed in… I think [PEP 531](https://www.python.org/dev/peps/pep-0531/) is the version currently under discussion. My impression is that most of the community thinks good use cases are rare enough that it's not worth adding to the language. You definitely have a use case here, but then expanding from 2 options to 3, or a dynamic list, would make `??` useless and a loop (as in David Maze's answer) TOOWTDI, so… maybe the majority is right. – abarnert Jul 31 '18 at 22:07
  • 2
    Actually, PEP 531 was withdrawn, and the earlier [PEP 505](https://www.python.org/dev/peps/pep-0505/) was updated and un-withdrawn, so… read 505, not 531. – abarnert Jul 31 '18 at 22:16
  • @Triggernometry `get_params` should raise an error if it is not able to retrieve the parameters from the database (i.e. if `params` is still `None` after both queries). `params = query_db(d['b'])` is useful in the example case of `d2`. – turnerm Jul 31 '18 at 22:27

1 Answers1

3

I think what you wrote initially is fine.

Another formulation that might work:

def get_params(d):
    for k in ['a', 'b']:
        params = query_db(d[k])
        if params is not None:
            return params
    raise KeyError("Params not found in db")
David Maze
  • 130,717
  • 29
  • 175
  • 215