P A N
P A N

Reputation: 5922

Python: "Multiple" multiple arguments in function

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?

Upvotes: 3

Views: 1940

Answers (2)

Raymond Hettinger
Raymond Hettinger

Reputation: 226674

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.

Upvotes: 2

Martijn Pieters
Martijn Pieters

Reputation: 1124708

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.

Upvotes: 5

Related Questions