for loop over function that returns a tuple?

Steven D'Aprano steve at pearwood.info
Wed Sep 2 16:49:35 CEST 2015


On Wed, 2 Sep 2015 09:49 pm, Victor Hooi wrote:

> I have a function which is meant to return a tuple:
> 
>     def get_metrics(server_status_json, metrics_to_extract, line_number):
>         <SOME_CODE>
>         return ((timestamp, "serverstatus", values, tags))

No need for the second pair of parentheses. Technically, no need for any
parens, this will do:

    return timestamp, "serverstatus", values, tags

but putting a single pair of parens around the four items is acceptable for
aesthetic reasons.


> I also have:
> 
>     def create_point(timestamp, metric_name, values, tags):

This function requires four separate values. So you have to either pass four
values, using tuple unpacking, or re-write the function to accept a single
tuple, then unpack it inside the function:

def create_point(items):
    timestamp, metric_name, values, tags = items
    ...


Another possibility is to be clever (perhaps too clever?) and accept either
four arguments or a single tuple of four arguments:

def create_point(*args):
    if len(args) == 1:
        timestamp, metric_name, values, tags = args[0]
    elif len(args) == 4:
        timestamp, metric_name, values, tags = args
    else:
        raise TypeError('too many or too few arguments')
    ...


But your current solution seems good to me. The create_point function is
self-documenting with four named arguments. You can pass the items
directly:

    pt = create_point(this, that, other, another)

or by unpacking a sequence:

    pt = create_point(*fourvalues)


Whatever you do, *something* has to unpack the four values. So why not do it
when you call the function?

There is one last possibility. I hesitate to mention it, because it is for
Python 2 only and is not supported in Python 3, but you can also do this:


# Python 2 only
def create_point((timestamp, metric_name, values, tags)):
    ...


Notice the extra pair of parens? This means that create_point now takes a
single argument, which is automatically unpacked into the four names given.
It is equivalent to the following version:

# Python 3 or Python 2
def create_point(items):
    timestamp, metric_name, values, tags = items
    ...

which I already showed.


> I am calling get_metric in a for loop like so:
> 
>     for metric_data in get_metrics(server_status_json, mmapv1_metrics,
>                                    line_number):
>         json_points.append(create_point(*metric_data))

This version looks fine to me. You're going to have to unpack the data
somewhere, this is as good as place as any other. Or you could write this:

# use nicer names, of course
for a, b, c, d in get_metrics(
        server_status_json, mmapv1_metrics, line_number):
    json_points.append(create_point(a, b, c, d))


It's all much the same whichever way you do it. I doubt there is any
efficiency difference, pick the version you like to read the most.


> I was hoping to use tuple unpacking to pass metric_data straight from
> get_metrics through to create_point.
> 
> However, in this case, metric_data only contains timestamp.

I don't understand this. 


-- 
Steven



More information about the Python-list mailing list