Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: how to use lambda or partial to bind arguments other than the first positional argument

I am having a lot of confusion in trying to use either a lambda or functools.partial to create a new function with bound positional arguments from an existing function.

I want to do something like this (which is not behaving as desired):

def addFunction(self, name, in_function, secondary_args=None, secondary_kwargs=None):

        # Assign the function with optional args based on whether any
        # optional args are not None

        if secondary_args is not None and secondary_kwargs is not None:
            func = lambda x: in_function(x, *secondary_args, **secondary_kwargs)
        elif secondary_args is None and secondary_kwargs is not None:
            func = lambda x: in_function(x, **secondary_kwargs)
        elif secondary_args is not None and secondary_kwargs is None:
            func = lambda x: in_function(x, *secondary_args)
        else:
            func = in_function
        ###

        func.__doc__ = in_function.__doc__
        self[name] = func # <-- This method is a class method for something deriving dict.

I've also tried replacing all of the lambda statements with equivalent functools.partial statements.

The problem is that if I use this function like this:

# Assume some_function takes 3 arguments, a float, a Bool, and a String,
# in that order.

someObject.addFunction("my_f", some_function, secondary_args=[True, "Option_A"])

now when I try to use (just for example) someObject["my_f"](5.0) it reports the first argument is True when I debug it.

It seems like the bindings, either with lambda or with partial simply push in the positional arguments and either only accept your extra positional argument at the end of *args or else just are dropping it (I'm not sure which).

For my application, since lots of functions will be stored in a particular object like this, with varying numbers of optional arguments chosen by a user, it's important that the function I get back, with bound arguments, still accepts the user's argument as the first positional argument, without resorting to forcing all arguments to be key-word arguments.

This seems like it should be simple enough. What am I missing?

like image 990
ely Avatar asked Aug 06 '12 15:08

ely


People also ask

Can we pass positional arguments in any order in Python?

Positional arguments must be passed in order as declared in the function. So if you pass three positional arguments, they must go to the first three arguments of the function, and those three arguments can't be passed by keyword.

How do you pass a positional argument in Python?

Functions can accept a variable number of positional arguments by using *args in the def statement. You can use the items from a sequence as the positional arguments for a function with the * operator. Using the * operator with a generator may cause your program to run out of memory and crash.

What is Functools partial?

You can create partial functions in python by using the partial function from the functools library. Partial functions allow one to derive a function with x parameters to a function with fewer parameters and fixed values set for the more limited function.

What is positional correspondence in Python?

A positional argument in Python is an argument whose position matters in a function call. You have likely used positional arguments without noticing. For instance: def info(name, age): print(f"Hi, my name is {name}.


1 Answers

Can you post a full script that reproduces the error? Because the following script works as expected:

class C(dict):                                                               
    def addFunction(self, name, in_function, secondary_args=None, secondary_kwargs=None):

        # Assign the function with optional args based on whether any        
        # optional args are not None                                         

        if secondary_args is not None and secondary_kwargs is not None:      
            func = lambda x, *secondary: in_function(x, *secondary_args, **secondary_kwargs)
        elif secondary_args is None and secondary_kwargs is not None:        
            func = lambda x: in_function(x, **secondary_kwargs)              
        elif secondary_args is not None and secondary_kwargs is None:        
            func = lambda x: in_function(x, *secondary_args)                 
        else:                                                                
            func = in_function                                               
        ###                                                                  

        func.__doc__ = in_function.__doc__                                   
        self[name] = func  # <-- This method is a class method for something deriving dict.


def f(x, y=0, z=1):                                                          
    print x, y, z                                                            


c = C()                                                                      
c.addFunction('my_f', f, secondary_args=[-1])                                
c['my_f'](0)                                                                 

# output is 0 -1 1, as expected                                              

Maybe the problem is in how you "attach" the function to your object?

like image 166
lbolla Avatar answered Oct 07 '22 23:10

lbolla