Reputation: 78863
I have Python v2.7.
I want to create a function which will accept None
, a str
or a list
. I need to append something to the parameter so I've created a helper function as follows:
def _append_or_set(x, to_append):
if x is None:
return to_append
if type(x) is str:
return [x, to_append]
if type(x) is list:
x.append(to_append)
return x
(Obviously this is not great programming, and there's no error handling etc. but it's just for illustration.)
But knowing Python, there is already a neat way of doing this. What is it?
I'm doing this because I want to filter a dataset. The caller passes either None (i.e. return all the rows) or a string (just rows matching this value) or a list (rows matching any of these values)
Regardless of what the caller asks for, I always need to include rows containing foo
at the very least. So I do:
def _filter_data(filter_by):
filter_by = _append_or_set(filter_by, 'foo')
return do_my_filtering(filter_by)
Upvotes: 0
Views: 685
Reputation: 21089
Python 3.4 adds a singledispatch
decorator to the functools
module, which lets you use a bit more of a generic programming style:
@singledispatch
def append_or_set(x, to_append):
raise RuntimeError("Unregistered type")
# This one isn't strictly necessary; you could just have the default behavior
# in the original function be to return the value to append if no other types
# will be supplied.
@append_or_set.register(type(None))
def append_or_set(x, to_append):
return to_append
@append_or_set.register(str)
def append_or_set(x, to_append):
return [x, to_append]
@append_or_set.register(list)
def append_or_set(x, to_append):
x.append(to_append)
return x
You can find more information in the docs, but the most relevant point is one not listed there: the implementation of functools.singledispatch
is all Python, so even though you're using Python 2.7 you can backport the necessary code from Python 3.4's version of functools.py
and use that. It shouldn't be too much trouble as the functions associated with singledispatch
don't appear to use any functionality from Python 3 that is significantly different from that usable in 2.7 even without __future__
imports (though I may well be wrong on that as I haven't tested it).
Also, as others have said, it may be a good idea to make the return values more consistent and, unless desired, avoid mutating arguments, e.g. return to_append
would become return [to_append]
and x.append(to_append);return x
would become return x + [to_append]
.
Upvotes: 0
Reputation: 388013
While your function would work fine (except that you should probably use isinstance
and elif
s), I do see a problem with it, and that is that it does not really have a consistent interface. Assuming that to_append
is a string, there are three different situations:
None
is passed, the function returns a string.Instead, you should try to keep the interface consistent. For example, always return a list and don’t touch the parameter itself:
def _append_or_set(x, to_append):
if x is None:
return [to_append]
elif isinstance(x, (list, tuple, set)): # also accept tuples and sets
return list(x) + [to_append]
else:
# assume that anything else is fine as the first element
return [x, to_append]
Upvotes: 2
Reputation: 5534
Its possible that I still may not be understanding what you want, but I think this will work:
>>> def _append_or_set(x, to_append):
... try:
... x.append(to_append)
... except AttributeError:
... if x:
... x = [x, to_append]
... else:
... x = to_append
... finally:
... return x
...
>>> _append_or_set([5,3,4], 6)
[5, 3, 4, 6]
>>> _append_or_set("this is x,", "this is appended")
['this is x,', 'this is appended']
>>> _append_or_set(None, "hello")
'hello'
By using try...except...finally
you can avoid explicit type checking which is generally considered bad practice.
The first try
assumes x
is a list. It isn't (if x.append()
breaks), then it must be a string, or None
. A simple if
will determine which it is.
Upvotes: 2