5

I'm using partial on a library function to supply it with a default value for one of the parameters

library_func = lambda x, y, z : x + y + z

my_func = functools.partial(library_func, z = 5) #default value for one of the variables
# equiv to lambda x, y : x + y + 5

Now suppose that instead of a constant default z, I want z to be dependant on the value of y. e.g., we look z up in a dictionary with keys that are different values of y.

z = {"1":7,"2":8}[str(y)]

# now my_func(x,y) = library_func(x,y,z = f(y))

Is that even possible? In my use case I've got a class holding a connection to a database (self.engine)

    self.engine = sqlalchemy.create_engine(connection_string)
    self.read_sql = partial(pd.read_sql, con = self.engine)

and pd.read_sql has a signature like pd.read_sql(con , name, columns ...)

I'd like to make a default value for columns which is dependent on the value of name passed.

I've thought about

self.read_sql = partial(pd.read_sql, con = self.engine, columns = lambda name: self.name_to_column_dict[name])

but get errors (for good reason!)

6
  • 1
    I think the easiest way would be to define a simple wrapper function that takes x and y, prepares z and calls your library function. Commented Dec 12, 2018 at 18:41
  • @Wombatz true, but if I do that won't I have to type out all the arguments by hand? That seems a bit round the houses Commented Dec 12, 2018 at 18:45
  • Wrapper function or decorator seems best to me. Avoid functools.partial except for the most trivial of cases. Commented Dec 12, 2018 at 18:50
  • @wim Without wishing to come across too newbie, a significant reason I like to use partial functions is because it keeps all the docstring and keyword arg stuff in Jupyter Notebook Intellisense/autocomplete. But as you say, maybe for more complex cases it's easier to roll your own. Commented Dec 12, 2018 at 18:54
  • To keep the docstring you can use functools.wraps. Commented Dec 12, 2018 at 21:01

2 Answers 2

5

You can use a wrapper function instead:

def my_func(x, y):
    return library_func(x, y, {"1":7,"2":8}[str(y)])

Or if you don't want to retype all the keyword arguments as you suggested in the comment, you can use inspect.signature to obtain the signature of your wrapper function and then bind the arguments being passed in to the parameters so you can access them with dict keys, no matter if the arguments are passed in as positional arguments or as keyword arguments:

import inspect

def my_func(*args, **kwargs):
    bound = sig.bind(*args, **kwargs)
    bound.apply_defaults()
    return library_func(**bound.arguments, z={"1":7,"2":8}[str(bound.arguments['y'])])

sig = inspect.signature(my_func)
Sign up to request clarification or add additional context in comments.

2 Comments

Ah, I see, that's one approach. But is there some approach which doesn't involve retyping all the arguments?
I've updated my answer with a solution that helps avoid retyping all the keyword arguments in the wrapper.
1

No, the default values for partial functions are evaluated before the function is called.

Ideally, you can modify the library function:

def library_func(x, y, z=None):
    if z is None:
        z = {'1': 7, '2': 8}[y]
    return  x + y + z

But if this isn't possible you can use a wrapper function:

def library_func(x, y, z):
    return  x + y + z

def library_func_wrapper(*args):
    return library_func(*args, {'1': 7, '2': 8}[str(args[-1])])

res = library_func_wrapper(6, 2)  # 16

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.