The typing module has List for this purpose
from typing import List
def f(param: List[str]):
pass
Answer from Iain Shelvington on Stack OverflowHow to pass list of string as function argument in python? - Stack Overflow
passing a list as function arguments
python - Create a function with two parameters: a list and a string, where the string has the following values - Code Review Stack Exchange
python - Defining a function whose arguments are given by a list of strings - Stack Overflow
Parameters that can be either a thing or an iterable or things are a code smell. It’s even worse when the thing is a string, because a string is an iterable, and even a sequence (so your test for isinstance(names, Iterable) would do the wrong thing).
The Python stdlib does have a few such cases—most infamously, str.__mod__—but most of those err in the other direction, explicitly requiring a tuple rather than any iterable, and most of them are considered to be mistakes, or at least things that wouldn’t be added to the language today. Sometimes it is still the best answer, but the smell should make you think before doing it.
I don’t know exactly what your use case is, but I suspect this will be a lot nicer:
def spam(*names):
namestr = ','.join(names)
dostuff(namestr)
Now the user can call it like this:
spam('eggs')
spam('eggs', 'cheese', 'beans')
Or, if they happen to have a list, it’s still easy:
spam(*ingredients)
If that’s not appropriate, another option is keywords, maybe even keyword-only params:
def spam(*, name=None, names=None):
if name and names:
raise TypeError('Not both!')
if not names: names = [name]
But if the best design really is a string or a (non-string) iterable of strings, or a string or a tuple of strings, the standard way to do that is type switching. It may look a bit ugly, but it calls attention to the fact that you’re doing exactly what you’re doing, and it does it in the most idiomatic way.
def spam(names):
if isinstance(names, str):
names = [names]
dostuff(names)
Using isinstance() to identify the type of input may provide a solution:
def generate_names(input_term):
output_list = []
if isinstance(input_term,str):
output_list.append(','.join(input_term.split()))
elif isinstance(input_term,list):
output_list.append(','.join(input_term))
else:
print('Please provide a string or a list.')
return(output_list)
This will allow you to input list, string of a single name, as well as string containing several names(separated by space):
name = 'Eric'
name1 = 'Alice John'
namelist = ['Alice', 'Elsa', 'George']
Apply this function:
print(generate_names(name))
print(generate_names(name1))
print(generate_names(namelist))
Get:
['Eric']
['Alice,John']
['Alice,Elsa,George']
plants escape snatch angle bedroom liquid oatmeal library flag touch
This post was mass deleted and anonymized with Redact
In Python, many prefer following the EAFP principle over type-checking (aka LBYL) — since exception handling is fairly cheap — see What is the EAFP principle in Python? specifically this answer.
Here's how to apply it to your sample code:
def getStuff(*stuff):
try:
stuff[0].split()
except AttributeError: # List objects have no split() method.
stuff = stuff[0]
for thing in stuff:
print(thing)
getStuff("cat", "mouse", "dog")
print()
animals = ['cow', 'horse', 'pig']
getStuff(animals)
Output:
cat
mouse
dog
cow
horse
pig
This takes the first element of the args tuple if it is a list, otherwise we can loop over the args (stuff) tuple itself:
def getStuff(*stuff):
stuff = stuff[0] if isinstance(stuff[0], list) else stuff
for thing in stuff:
print(thing)
More elegant solution, using itertools:
import itertools
def getStuff(*stuff):
for thing in itertools.chain.from_iterable(stuff):
print(thing)
Explanation: itertools.chain.from_iterable just flattens the nested iterable, in case stuff is not just a tuple of strings. Like this it doesn't matter whether stuff is a tuple or a list in a tuple, or even a tuple of multiple lists.
To expand a little on the other answers:
In the line:
def wrapper(func, *args):
The * next to args means "take the rest of the parameters given and put them in a list called args".
In the line:
func(*args)
The * next to args here means "take this list called args and 'unwrap' it into the rest of the parameters.
So you can do the following:
def wrapper1(func, *args): # with star
func(*args)
def wrapper2(func, args): # without star
func(*args)
def func2(x, y, z):
print x+y+z
wrapper1(func2, 1, 2, 3)
wrapper2(func2, [1, 2, 3])
In wrapper2, the list is passed explicitly, but in both wrappers args contains the list [1,2,3].
The simpliest way to wrap a function
func(*args, **kwargs)
... is to manually write a wrapper that would call func() inside itself:
def wrapper(*args, **kwargs):
# do something before
try:
return func(*a, **kwargs)
finally:
# do something after
In Python function is an object, so you can pass it's name as an argument of another function and return it. You can also write a wrapper generator for any function anyFunc():
def wrapperGenerator(anyFunc, *args, **kwargs):
def wrapper(*args, **kwargs):
try:
# do something before
return anyFunc(*args, **kwargs)
finally:
#do something after
return wrapper
Please also note that in Python when you don't know or don't want to name all the arguments of a function, you can refer to a tuple of arguments, which is denoted by its name, preceded by an asterisk in the parentheses after the function name:
*args
For example you can define a function that would take any number of arguments:
def testFunc(*args):
print args # prints the tuple of arguments
Python provides for even further manipulation on function arguments. You can allow a function to take keyword arguments. Within the function body the keyword arguments are held in a dictionary. In the parentheses after the function name this dictionary is denoted by two asterisks followed by the name of the dictionary:
**kwargs
A similar example that prints the keyword arguments dictionary:
def testFunc(**kwargs):
print kwargs # prints the dictionary of keyword arguments
I think your best bet is to cast a single string to a 1-element list, and then have the rest of your function deal exclusively with lists.
def add_to_db(name_or_names):
import types
if isinstance(name_or_names, types.StringTypes):
name_or_names = [name_or_names]
try:
for name in name_or_names:
add_name(name)
commit_to_db()
except TypeError:
# we didn't get a string OR a list >:(
Use keyword arguments:
def add_to_db(name=None, names=None):
if name:
.....
elif names:
....
else:
raise ValueError("Invalid arguments")
add_to_db(name='John')
add_to_db(names=['explicit', 'is', 'better'])