3

I'm a Python newbie, but I know that I can allow a variable number of multiple arguments in a function using *args.

This script looks for a word in any number of string *sources:

def find(word, *sources):
    for i in list(sources):
        if word in i:
            return True

source1 = "This is a string"
source2 = "This is Wow!"

if find("string", source1, source2) is True:
    print "Succeed"

However, is it possible to specify "multiple" multiple arguments (*args) in one function? In this case, that would be looking for multiple *words in multiple *sources.

As in, figuratively:

if find("string", "Wow!", source1, source2) is True:
    print "Succeed"
else:
    print "Fail"

How can I make the script discern what is intended to be a word, and what's supposed to be a source?

1
  • If you have multiple multiple arguments how would you call the function? How would python know which one to put which value in? Commented Aug 22, 2015 at 16:34

2 Answers 2

5

No, you can't, because you cannot distinguish where one type of element stops and the other starts.

Have your first argument accept either a single string or a sequence, instead:

def find(words, *sources):
    if isinstance(words, str):
        words = [words]  # make it a list
    # Treat words as a sequence in the rest of the function

Now you can call it either as:

find("string", source1, source2)

or

find(("string1", "string2"), source1, source2)

By passing in a sequence explicitly, you can distinguish it from the multiple sources as it is in essence just one argument.

Sign up to request clarification or add additional context in comments.

2 Comments

@VigneshKalai: in which case the voter could be more helpful by flagging the question as a duplicate. I doubt that that was their motivation however.
Where more than one type of argument is to be expected, my experience tells me to completely avoid * and ** magics. It gets messy when you use keyword arguments, and does not feel completely "consistent", like in your example: words have to be passed as iterable, sources as varargs. It may not look so pythonic to write def find(words, sources) but in the long term the consistency and cleanliness beats the cool syntax.
2

The usual solution to needing "multiple multiple sources" is to have *args be the first multiple and the second multiple being tuples.

>>> def search(target, *sources):
        for i, source in enumerate(sources):
            if target in source:
                print('Found %r in %r' % (i, source))
                return
        print('Did not find %r' % target)

You will find other examples of this kind of API design used throughout the Python core language:

>>> help(str.endswith)
Help on method_descriptor:

endswith(...)
    S.endswith(suffix[, start[, end]]) -> bool

    Return True if S ends with the specified suffix, False otherwise.
    With optional start, test S beginning at that position.
    With optional end, stop comparing S at that position.
    suffix can also be a tuple of strings to try.

>>> 'index.html'.endswith(('.xml', '.html', '.php'), 2)
True        
    >>> search(10, (5, 7, 9), (6, 11, 15), (8, 10, 14), (13, 15, 17))
    Found 2 in (8, 10, 14)

Note that suffix can be a tuple of strings to try.

1 Comment

I fail to see how this answers the question which is that the OP effectively wants to be able to handle *targets and *sources.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.