576i
576i

Reputation: 8352

Better / More pythonic way to process the results of a function with multiple return values

The code below works, but looks very ugly. I'm looking for a more pythonic way to write the same thing.

The goal: React on a result of a function that returns multiple values.

Example function

def myfilterfunc(mystr):
   if 'la' in mystr:
       return True, mystr
   return False, None

This returns True and a string (if the string cointains "la"), or False and nothing.

In a second function, I'm passing myfilterfunc as an optional parameter

def mymainfunc(mystr,filterfunc=None):

This function fills a returnlist. If no function is given, the result is not filtered and added as is. If a filter function is given, if the filter function returns True, a returned string is added. (This is just an example that would easily work with one return value, but I'm trying to get the systax right for a more complicated setup)

if filterfunc:
    tmp_status,tmp_string = filterfunc(mystr[startpos:nextitem])
    if tmp_status:
        returnlist.append(tmp_string)
    else:
        returnlist.append(mystr[startpos:nextitem])

Any idea how I can write this without using temporary variables to store the return values of the function?

Full "working" test code below

def string2list(mystr,splitlist,filterfunc=None):
    returnlist = []
    startpos = 0
    nextitem = -1
    matched = True
    while matched:
        matched = False
        for sub in splitlist:
            if startpos == 0:
                tmpi = mystr.find(sub)
            else:    
                tmpi = mystr.find(sub,startpos + 1)
            if (tmpi > 0) and ((nextitem < 0) or (nextitem > tmpi)):
                nextitem = tmpi
                matched = True
        if filterfunc:
            tmp_status,tmp_string = filterfunc(mystr[startpos:nextitem])
            if tmp_status:
                returnlist.append(tmp_string)
        else:
            returnlist.append(mystr[startpos:nextitem])
        startpos = nextitem 
        nextitem = -1
    return returnlist

def myfilterfunc(mystr):
    if 'la' in mystr:
        return True,mystr
    return False,''    


splitlist = ['li','la']
mytext = '''
li1
li2
li3
fg4
fg5
fg6
la7
la
la
tz
tz
tzt
tz
end
'''



print string2list(mytext,splitlist)
print
print string2list(mytext,splitlist,myfilterfunc)

Upvotes: 0

Views: 103

Answers (2)

Blckknght
Blckknght

Reputation: 104702

I think there are two better approaches to your problem that don't involve using two return values.

The first is to simply return a Boolean value and not a string at all. This works if your filter is always going to return the string it was passed unmodified if it returns a string at all (e.g. if the first value is True). This approach will let you avoid using temporary values at all:

if filterfunc:
    if filterfunc(mystr[startpos:nextitem]):
        returnlist.append(mystr[startpos:nextitem])

(Note, I'd suggest renaming filterfunc to predicate if you go this route.)

The other option will work if some filterfunc might return a different second value than it was passed under some situations, but never the 2-tuple True, None. In this approach you simply use the single value as both the signal and the payload. If it's None, you ignore it. If it's anything else, you use it. This does require a temporary variable, but only one (and it's a lot less ugly).

if filterfunc:
    result = filterfunc(mystr[startpos:nextitem])
    if result is not None:
        returnlist.append(result)

Upvotes: 0

6502
6502

Reputation: 114461

If this is going to happen often you can factor out the uglyness:

def filtered(f, x):
    if f:
        status, result = f(x)
        return result if status else x
    else:
        return x

used like

returnlist.append(filtered(filterfunc, mystr[startpos:nextitem]))

so that if you have many similar optional filters the code remains readable. This works because in Python functions/closures are first class citizens and you can pass them around like other values.

But then if the logic is about always adding (either the filtered or the unfiltered) why not just write the filter to return the input instead of (False, "") in case of failure?

That would make the code simpler to understand...

returnlist.append(filterfunc(mystr[startpos:nextitem]))

Upvotes: 1

Related Questions