2

In refactoring some code I found a helper method get_records_from_file that has a complex return signature:

class RecordDefinition:

    def __init__(self):
        self.foo = None
        self.bar = None

def get_records_from_file(file_name) -> Dict[int, List[Dict[str, RecordDefinition]]]:
...

After some inspection I understand that this return type really means:

Dict[record_group_id, List[Dict[record_type_id, RecordDefinition]]]

In other projects (and other languages) I've seen this very thing change into:

Dict[record_group_id, List[RecordTypeCollection]]

Which leads to:

Dict[record_group_id, RecordCollection]

And finally ending in:

RecordGroupCollection

Generally, Is this kind of replacement desirable? More explicitly, should I change complex return objects like this to Object Oriented Classes? I see some benefit here that allows structures to be more explicitly named. But this adds a lot of code in the form of smaller classes.

In python, I get the impression that this kind of class creation is frowned upon. Is my assumption correct?

7

I think PEP 20 can provide some guidance here.

Explicit is better than implicit.

Creating a wrapper class for this data structure has the benefit of making the code more explicit through naming the structure and its component fields.

This might be opinionated, but I think the following also apply to your example:

Simple is better than complex.

Complex is better than complicated.

Flat is better than nested.

Sparse is better than dense.

A class with methods and fields would be less "nested" than the raw data structure.

However...

[...] practicality beats purity.

I don't think it's safe to say as a hard-and-fast rule that you should always wrap nested structures. A wrapper class could be overkill if it's wrapping a very simple structure (e.g., perhaps if the method were just returning a tuple of a list and another data type). It's also arguable that if the class has no better business-domain name than Collection, it might be too abstract to add value.

The question to ask when evaluating whether to create a class in these situations is, do you think more developers will find it easier to work with the new class or the original data structure?

In the example you've provided, I think it's very reasonable (and in my opinion preferable) to create the new class.

2

Well, yes, but in cases similar to your example you may be better of with a cursor or enumerator because this (wrapping it all up in a complex object) does not scale very well. Likewise, if you need to pass data from a file, you could return a stream interface instead of all the data from the file wrapped in some structure. The latter solution would potentially require an enormous in-memory buffer.

In OO terms this is a form of aggregation over containment. There are trade offs, returning an open stream would transfer the responsibility of disposing it (closing the file) to the receiver, which makes error handling more complex. So it depends on what you can expect in terms of sheer data size and how you will use that data (all at once or piecemeal over the duration of the session). There is no universally applicable answer.

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.