tags:

views:

75

answers:

4

Hello,

I am creating a public method to allow callers to write values to a device, call it write_vals() for example.

Since these values will by typed live, I would like to simplify the user's life by allowing them type in either a list or a single value, depending on how many values they need to write. For example:

write_to_device([1,2,3])

or

write_to_device(1)

My function would like to work with a flat list, so I tried to be clever and code something like this:

input_list = []  
input_list.extend( input_val )

This works swimmingly when the user inputs a list, but fails miserably when the user inputs a single integer:

TypeError: 'int' object is not iterable

Using list.append() would create a nested list when a list was passed in, which would be an additional hassle to flatten.

Checking the type of the object passed in seems clumsy and non-pythonic and wishing that list.extend() would accept non-iterables has gotten me nowhere. So has trying a variety of other coding methods.

Suggestions (coding-related only, please) would be greatly appreciated.

+5  A: 

You can check if the passed parameter is a list with the isinstance() function.

An even better solution could be to support a variable number of arguments:

def write_to_device(*args):
   # |args| is now a list of all the arguments given to the function
   input_list.extend(args)

This way multiple values can be given to the function even without having to explicitly specify them as a list:

write_to_device(1)
write_to_device(1,2,3)
sth
If you want to pass a list to write_to_device, you can do so using *: write_to_device(*mylist)
Frederik
write_to_device(*args) signify to the user that passing no write value is legal, which in this case it is not.
JS
@JS: You could add a check like `if len(args)==0: raise TypeError('Missing arguments')`
sth
+1  A: 

You can use try...except...:

try:
    input_list = list(input_val)
except TypeError:
    input_list = list((input_val,))

This is pythonic.

P.S.: Variable parameters, as @sth proposes, is much better, but I leave this answer for completeness. Sometimes you cannot use variable parameters.

Felix Kling
I had hoped to find a less wordy solution, but this one works well. I do like your 'list((input_val,))' trick. Thank you!
JS
This breaks if a single string is passed to the function, like 'abc'.
Chris B.
+3  A: 

Instead of checking the type, you could use try, except. Although, I'm curious to see how people better at python than I am would do this.

input_list = []
try:
    input_list.extend(input_val)
except TypeError:
    input_list.append(input_val)

This seems incomplete to me from an error-checking standpoint, but if you know users will only ever input a single value or a list, this will work.

Jacinda S
+1 but you should use `except TypeError:`
Zach
@Zach Thanks for catching that!
Jacinda S
A: 

There are a few ways to do this, and which you choose depends on your needs.

You could try and convert the argument to a list, and assume if it doesn't work it should be interpreted as a single argument:

def write_to_device(args):
    try:
        args = list(args)
    except TypeError:
        args = [args]

    print args

This works pretty well, but you might run into problems if you pass a string as an argument:

>>> write_to_device(1)
[1]
>>> write_to_device([1,2])
[1, 2]
>>> write_to_device('abc')
['a', 'b', 'c']

You can correct for that by using isinstance to check if the argument is a string:

def write_to_device(args):
    if isinstance(args, basestring):
        args = [args]
    else:
        try:
            args = list(args)
        except TypeError:
            args = [args]

    print args

Which gives you:

>>> write_to_device(1)
[1]
>>> write_to_device([1,2])
[1, 2]
>>> write_to_device('abc')
['abc']  

As someone noted, you can allow your function to take an arbitrary number of arguments:

def write_to_device(*args):
    print args

Which gets you:

>>> write_to_device(1)
(1,)
>>> write_to_device([1,2])
([1, 2],)
>>> write_to_device('abc')
('abc',)
>>> write_to_device(*[1,2])
(1, 2)

My recommended way is just to require a list be passed to the function:

write_to_device([1])
write_to_device([1, 2, 3])

This is simple, straight-forward, and unambiguous.

Chris B.
I like your first suggestion. Clean and elegant. The user will be passing in a single or list of hex values, so the script blowing up if they pass a string is just desserts. ;-)Ok, ok. I'll check for a string and fail gracefully (grumble) :-)
JS