2
\$\begingroup\$

I would like to make this code more efficient, I have isolated the problem as being these two for loops:

    for i, device in enumerate(list_devices):
        for data_type_attr_name in data_types:
            result_list_element = {
                "device_reference": device.name,
                "device_name": "REF - " + device.name,
                "data_type": data_type_attr_name,
                "type": next(
                    (data_type["type"] for data_type in DATA_TYPES if data_type["name"] == data_type_attr_name)
                ),
                "data_points": getattr(device, data_type_attr_name)(
                    is_last_value=is_last_value,
                    from_timestamp=from_timestamp,
                    to_timestamp=to_timestamp,
                    aggregate_period_name=aggregate_period_name,
                    aggregate_operation_name=aggregate_operation_name,
                    decimal_places=decimal_places,
                ),
            }
            if not isinstance(result_list_element["data_points"], list):
                raise TypeError("`data_points` must be returned as a list, even if it contains only one element.")
            result_list.append(result_list_element)

    return result_list

list_devices is a list of Django model objects, data_types is a list of strings, each one representing a data type.

Is there any way of losing one of the for loops while maintaining the same output?

Thanks

\$\endgroup\$
2
\$\begingroup\$

Use itertools.product

import itertools

for device, data_type_attr_name in itertools.product(list_devices, data_types):
    result_list_element = {
        "device_reference": device.name,
        "device_name": "REF - " + device.name,
        "data_type": data_type_attr_name,
        "type": next((data_type["type"] for data_type in DATA_TYPES if data_type["name"] == data_type_attr_name)),
        "data_points": getattr(device, data_type_attr_name)(
            is_last_value=is_last_value,
            from_timestamp=from_timestamp,
            to_timestamp=to_timestamp,
            aggregate_period_name=aggregate_period_name,
            aggregate_operation_name=aggregate_operation_name,
            decimal_places=decimal_places,
        ),
    }
    if not isinstance(result_list_element["data_points"], list):
        raise TypeError("`data_points` must be returned as a list, even if it contains only one element.")
    result_list.append(result_list_element)
return result_list
\$\endgroup\$
3
\$\begingroup\$

Assume you are referring to speed, not conciseness when asking about efficiency. In which case reducing the amount of loops will not necessarily improve performance.

"type": next((data_type["type"] for data_type in DATA_TYPES if data_type["name"] == data_type_attr_name))

This can be improved so that you are not needing to go over DATA_TYPES looking for a matching name to data_type_attr_name every iteration. Instead you can make a reverse lookup dictionary once before the loop where data_type["name"] is the key.

Whenever you are searching through a list often, it can be very beneficial to create a dictionary.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.