3

I have a function that should be able to take either many string arguments as *args, or a list of strings as an argument. For example:

def getStuff(*stuff):
  for thing in stuff:
    print(thing)
getStuff("cat", "mouse", "dog")
getStuff(animals)

I would like for this function to able to produce the same result if I call it either way. There is the following very simple method that I'm currently using but doesn't make for the cleanest code:

def getStuff(*stuff):
  if type(stuff[0]) != list:
    for thing in stuff:
        print(thing)
  else:
    for thing in stuff:
      for subthing in thing:
        print(subthing)

Is there a simple way to accomplish this? I'm looking for python best practices.

martineau
  • 119,623
  • 25
  • 170
  • 301
dm0413
  • 43
  • 4

3 Answers3

2

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

martineau
  • 119,623
  • 25
  • 170
  • 301
1

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.

mcsoini
  • 6,280
  • 2
  • 15
  • 38
  • if `stuff[0]` is a list then they probably also want to iterate over the rest of the list (e.g. `stuff[1]` etc. – Chris_Rands May 01 '21 at 17:29
  • Also isinstance() is not *better* only different in the case of handling subclassing etc. – Chris_Rands May 01 '21 at 17:30
  • @Chris_Rands Doesn't sound like it: "either many string arguments as *args, or a list of strings as an argument" – mcsoini May 01 '21 at 17:30
  • maybe- it depends if you read that description or try to match the way they implemented their function – Chris_Rands May 01 '21 at 17:32
  • I rather go by the description, which is sufficiently clear. The implementation has issues in the first place, otherwise they wouldn't be asking. – mcsoini May 01 '21 at 17:34
0

When you're writing a function in Python, usually the best practice is to assume that the parameter(s) for your function are known and unchanging.

Looking at your code, it seems like the same thing can accomplished with your function if you pass it an array of strings no matter what. After passing it the array, just print each item within it. If there is just one item, it will work the same as a function that just prints that single item.

I would suggest writing it like this:

def getStuff(stuff):
    for thing in stuff:
        print(thing)

I do realize that this isn't exactly what you're looking for, but when talking about the best practices in Python, this would be it.

bperry14
  • 55
  • 6
  • Adding on to this answer, there are a few reasons to do it this way. First, it's obviously cleaner and easier to read. Second, when you pass arguments to a function, you use them by calling the reference to those objects, usually with a variable. This means that you cannot iterate over the parameters; they have to be in array anyway. – bperry14 May 01 '21 at 17:23
  • You might want to have a look at https://stackoverflow.com/questions/33542959/why-use-packed-args-kwargs-instead-of-passing-list-dict for a different opinion. – Thierry Lathuille May 01 '21 at 17:30