Python class – collections abc

Time:2022-5-2

Classes are very important for our daily programming. You will find that we define classes most of the time. Class contains data and uses methods to describe the interaction between data. Therefore, from a certain level, classes in Python are containers used to encapsulate attributes and functions. Python built-in container types:list tuple set dict, you can directly inherit the above easy types only when the subclass is very simple.

FrequencyListSubclasses inherit fromlist, gotlistAll applicable APIs; And also realized its own unique methodfrequency, that is, statistics in the form of a dictionarylistFrequency of occurrence of elements in:

class FrequencyList(list):
    def __init__(self, nums):
        super().__init__(nums)
        
    def frequency(self):
        counts = {}
        for item in self:
            counts.setdefault(item, 0)
            counts[item] += 1
        return counts

Operation results:

>> L = FrequencyList(range(5))
>> L
[0, 1, 2, 3, 4]
>> len(L)
5
>> L.extend(range(3))
>> L
[0, 1, 2, 3, 4, 0, 1, 2]
>> L.frequency()
{0: 2, 1: 2, 2: 2, 3: 1, 4: 1}

I Custom container type

Next, we will define a binary tree container in the form of implementing a special method, which can be accessed using subscripts like a sequence. First, the base classBinaryNodeDefinition of:

class BinaryNode:
    def __init__(self, left=None, right=None, value=None):
        self.left = left
        self.right = right
        self.value = value

usetree[i]To access the value on the node in the binary tree, which is actually calledtree.__getitem__(i)。 Therefore, next defineIndexableNodeSubclass, and implement__getitem__method:

class IndexableNode(BinaryNode):
    def _traverse(self):
        if self.left is not None:
            yield from self.left._traverse()
        yield self
        if self.right is not None:
            yield from self.right._traverse()
            
    def __getitem__(self, index):
        for i, item in enumerate(self._traverse()):
            if i == index:
                return item.value
        raise IndexError(f'Index {index} is out of range')

Note:_traverseIs a recursive method that accesses the values on nodes in the order of left first and depth first.
item base IndexableNodeBinary tree:

>> root = IndexableNode(value=1)
>> root.left = IndexableNode(left=IndexableNode(value=20), value=2, right=IndexableNode(value=21))
>> root.right = IndexableNode(value=31)

At this point, we can traverse the node, access the node through subscript and usein/not inOperator:

>> list(root)
[20, 2, 21, 1, 31]
>> root[0]
20
>> 20 in root
True

If you still want to uselenTo calculate the number of binary tree nodes, it must also be realized__len__method:

class SequenceNode(IndexableNode):
    def __len__(self):
        for count, _ in enumerate(self._traverse(), 1):
            pass
        return count

item base SequenceNodeBinary tree:

>> root2 = SequenceNode(value=1)
>> root2.left = SequenceNode(left=SequenceNode(value=20), value=2, right=SequenceNode(value=21))
>> root2.right = SequenceNode(value=31)

Number of calculation nodes:

>> list(root2)
[20, 2, 21, 1, 31]
>> len(root2)
5

But in fact, it did__len__After that,SequenceNodeThe function of is still not perfect. You want to use it like the built-in sequence typeindex countAnd other functions, we still need to continue to implement special methods. Python provides us with a powerful tool for this purposecollections.abcmodular.

II collections. abc

collections.abcThe module defines a series of abstract base classes and provides common methods for each container type. After inheriting subclasses from these base classes, if you forget to implement a method, an exception will be thrownTypeErrorException:

from collections.abc import Sequence

class BadType(Sequence):
    pass

Operation results:

>> bad_o = BadType()
...
TypeError: Can't instantiate abstract class BadType with abstract methods __getitem__, __len__

As defined aboveSequenceNodeType because we implement the two methods mentioned in the exception__getitem__and__len__So, if we define a new subclassBetterTypeInherited fromSequenceandSequenceNode

class BetterNode(SequenceNode, Sequence):
    pass

BetterNodeThe created instance can have sequence related APIs like Python’s built-in sequence types:

>> root3 = BetterNode(value=1)
>> root3.left = BetterNode(left=BetterNode(value=20), value=2,  right=BetterNode(value=21))
>> root3.right = BetterNode(value=31)

Operation results:

>> list(root3)
[20, 2, 21, 1, 31]
>> root3.count(2)
1
>> root3.index(21)
2

When customizing container types, you cancollections.abcThese base classes can ensure that our subclasses have corresponding interfaces.

Attachment: forSet MutableMappingFor more complex container types, if you do not inherit abstract classes, you must implement many special methods to make the custom types conform to python programming habits.