hgajshb
hgajshb

Reputation: 225

Extending Types by Subclassing in Python

This is extracted from Learning Python 4th edition. Its function is to subclass set using list. But I don't understand line 5 list.__init__([]), please help. The code works even when I commented out this line of code. Why?

### file: setsubclass.py

class Set(list):
    def __init__(self, value = []):      # Constructor
        list.__init__([])                # Customizes list 
        self.concat(value)               # Copies mutable defaults

    def intersect(self, other):          # other is any sequence
        res = []                         # self is the subject
        for x in self:
            if x in other:               # Pick common items
                res.append(x)
        return Set(res)                  # Return a new Set

    def union(self, other):              # other is any sequence
        res = Set(self)                  # Copy me and my list
        res.concat(other)
        return res

    def concat(self, value):             # value: list, Set . . .
        for x in value:                  # Removes duplicates
            if not x in self:
                self.append(x)

    def __and__(self, other): return self.intersect(other)
    def __or__(self, other):  return self.union(other)
    def __repr__(self):       return 'Set:' + list.__repr__(self)

if __name__ == '__main__':
    x = Set([1,3,5,7])
    y = Set([2,1,4,5,6])
    print(x, y, len(x))
    print(x.intersect(y), y.union(x))
    print(x & y, x | y)
    x.reverse(); print(x)
    x

Upvotes: 3

Views: 278

Answers (4)

Martijn Pieters
Martijn Pieters

Reputation: 1121884

You mean list.__init__([])?

It calls the base initializer from the subclass initializer. Your subclass has replaced the base class initializer with its own.

In this case commenting that out happens to work because the unbound initializer was called with an empty list instead of self and is thus a no-op. This is an error on behalf of the authors, most likely.

But it is a good idea generally to make sure the base class has run its initialization code when you subclass. That way all internal data structures the base class methods rely on have been set up correctly.

Upvotes: 3

Lauritz V. Thaulow
Lauritz V. Thaulow

Reputation: 50995

The code in the book contains an error. I've submitted an errata to O'Reilly books, which you can read along with the authors response on this page (search for 982). Here's a small snippet of his response:

This code line has apparently been present in the book since the 2nd Edition (of 2003--10 years ago!), and has gone unremarked by hundreds of thousands of readers until now

The line list.__init__([]) is missing an argument, and commenting it out makes no difference whatsoever, except speeding up your program slightly. Here's the corrected line:

        list.__init__(self, [])

When calling methods that are not static methods or class methods directly on class objects, the normally implicit first argument self must be provided explicitly. If the line is corrected like this it would follow the good practice that Antonis talks about in his answer. Another way to correct the line would be by using super, which again makes the self argument implicit.

        super(Set, self).__init__([])

The code in the book provides a different empty list ([]) as the self argument, which causes that list to be initialized over again, whereupon it is quickly garbage collected. In other words, the whole line is dead code.

To verify that the original line has no effect is easy: temporarily change [] in list.__init__([]) to a non-empty list and observe that the resulting Set instance doesn't contain those elements. Then insert self as the first argument, and observe that the items in the list are now added to the Set instance.

Upvotes: 6

Antonis Christofides
Antonis Christofides

Reputation: 6939

You mean this line?

    list.__init__([])

When you override the __init__ method of any type, it's good practice to always call the inherited __init__ method; that is, the __init__ method of the base class. This way you perform initialization of the parent class, and add initialization code specific to the child class.

You should follow this practice even if you are confident that the __init__ of the parent does nothing, in order to ensure compatibility with future versions.

Update: As explained by Lauritz in another answer, the line

    list.__init__([])

is wrong. See his answer and the other answers for more.

Upvotes: 4

DevLounge
DevLounge

Reputation: 8437

This line is like creating an __init__ constructor in Set class which calls its base class constructor.

You might have already seen this:

class Set(list):
...   
def __init__(self, *args, **kwargs):
        super(Set, self).__init__(args, kwargs)
        # do something additional here

Upvotes: 1

Related Questions