views:

293

answers:

4

I have a Python function in which I am doing some sanitisation of the input parameters:

def func(param1, param2, param3):
    param1 = param1 or ''
    param2 = param2 or ''
    param3 = param3 or ''

This caters for the arguments being passed as None rather than empty strings. Is there an easier/more concise way to loop round the function parameters to apply such an expression to all of them. My actual function has nine parameters.

A: 

You probably want something more like this:

def func(param1='', param2='', param3=''):

It's far more concise and idiomatic.

See http://docs.python.org/tutorial/controlflow.html#default-argument-values

Kamil Kisiel
I have some default argument values in my real function, but I am still concerned where parameters are passed with a value of 'None'. For example, I could call func(None, None).
Mat
That won't do what he wants -- you could still pass 'None' as a parameter.
mipadi
Right, sorry, I misunderstood the question. In that case, I recommend Kiv's answer below.
Kamil Kisiel
A: 
def func(x='', y='', z='hooray!'):
    print x, y, z

In [2]: f('test')
test  hooray!

In [3]: f('test', 'and')
test and hooray!

In [4]: f('test', 'and', 'done!')
test and done!
llimllib
+2  A: 

You could do some list manipulation:

def func(param1, param2, param3):
    param1, param2, param3 = map(lambda x: x or '', (param1, param2, param3))

but I'm not sure that's better than just writing out the nine lines, since once you get to nine parameters, that's a heinously long line.

You could change the declaration of the function:

def func(*args):
    param1, param2, param3 = map(lambda x: x or '', args)

but then you lose the documentation that comes from having real parameter names, as well as the possibility of changing the defaults, etc. And you still have a pretty fugly line there to unpack them.

I say write out the nine lines, or change the function to have fewer parameters: nine is kind of a lot anyway!

Ned Batchelder
+5  A: 

This looks like a good job for a decorator. How about this:

def sanitized(func):
    def sfunc(*args, **kwds):
        return func(*[arg or '' for arg in args],
                    **dict((k, v or '') for k,v in kwds.iteritems()))
    sfunc.func_name = func.func_name
    sfunc.func_doc = func.func_doc
    return sfunc

You would use this on your function like so:

@sanitized
def func(param1, param2, param3):
    print param1, param2, param3

Then the parameters will be replaced by the empty string if they are false:

>>> func('foo', None, 'spam')
foo  spam

(Note that this will still mess up the function signature as Ned Batchelder points out in his answer. To fix that you could use Michele Simionato's decorator module-- I think you'd just need to add a @decorator before the definition of sanitized)

dF
Does this work with keyword arguments though?
Kiv
I think you also need to pass along the kwargs too: **dict((k, arg or '') for k, arg in kwargs.items()))
Kiv
Fixed to support kwargs, thanks Kiv
dF
Yes, this is by far the best solution (other than refactoring the original code to not require it.)
Kiv
you should probably change the `arg or ''` to `'' if arg is None else arg` or something similar just in case arg is 0 or {} or something non-None.
Autoplectic