Should I subclass a Python list or create a class with a list as an attribute?

I need a container that can collect multiple objects and provides some reporting functions for container elements. Essentially, I would like to be able to:

magiclistobject = MagicList() magiclistobject.report() ### generates all my needed info about the list content 

So, I was thinking of subclassing a regular list and adding the report () method. Thus, I use all the functions of the built-in list.

 class SubClassedList(list): def __init__(self): list.__init__(self) def report(self): # forgive the silly example if 999 in self: print "999 Alert!" 

Instead, I could also create my own class with the magiclist attribute, but then I would need to create new methods for adding, expanding, etc., if I want to get into the list using:

 magiclistobject.append() # instead of magiclistobject.list.append() 

I will need something like this (which seems redundant):

 class MagicList(): def __init__(self): self.list = [] def append(self,element): self.list.append(element) def extend(self,element): self.list.extend(element) # more list functionality as needed... def report(self): if 999 in self.list: print "999 Alert!" 

I thought that subclassing the list would be no problem. But this post here makes it sound no-no. Why?

+5
source share
2 answers

One of the reasons why expanding a list can be bad is because it links your MagicReport object too tightly to this list. For example, the Python list supports the following methods:

 append count extend index insert pop remove reverse sort 

It also contains a number of other operations (adding, comparing using < and > , slicing, etc.).

Are all these operations the things that your MagicReport object really wants to support? For example, the following is legitimate Python:

 b = [1, 2] b *= 3 print b # [1, 2, 1, 2, 1, 2] 

This is a pretty far-fetched example, but if you inherit from a list, your MagicReport object will do the same if someone inadvertently does something like this.

As another example, what if you try to slice a MagicReport object?

 m = MagicReport() # Add stuff to m slice = m[2:3] print type(slice) 

You probably expect the slice to be another MagicReport object, but this is actually a list. You need to override __getslice__ to avoid unexpected behavior that is a little painful.


It also makes it difficult for you to change the implementation of the MagicReport object. If you need to do more complex analysis, it often helps to change the data structure into something more suitable for the problem.

If the list is a subclass, you can work around this problem by simply providing new methods append , extend , etc. so that you do not change the interface, but you will not have a clear way to determine which of the list methods are actually used if you are not reading the whole code base. However, if you use a composition and just have a list as a field and create methods for supported operations, you know exactly what needs to be changed.

I actually came across a scenario very similar to yours at work recently. I had an object that contained a collection of “things”, which I first imagined within myself as a list. As the requirements of the project change, I eventually changed the object to an internal use of dict, a custom collection object, and then finally OrderedDict in quick succession. At least in my experience, composition makes it much easier to change how something is implemented, rather than inheritance.


I believe that a list extension can be approved in scenarios where your MagicReport object is a legitimate list in all but the name. If you want to use MagicReport as a list in each individual case and do not plan to change its implementation, then this may be more convenient for a list of subclasses and just do with it.

Although in this case it would be better to just use the list and write the “report” function - I can’t imagine that you need to report the contents of the list more than once and create a custom object using a custom method for this purpose only may be redundant (although this obviously depends on what exactly you are trying to do)

+6
source

As a rule, whenever you ask yourself, “should I inherit or have a member of this type”, choose not to inherit. This rule of thumb is known as the “composition of preference over inheritance”.

The reason why this is so: composition is suitable if you want to use functions of another class; inheritance is appropriate if other code is to use the functions of another class with the class you are creating.

+3
source

Source: https://habr.com/ru/post/1200419/


All Articles