Абстрактные классы в стандартной библиотеке Python#
В стандартной библиотеке Python есть несколько готовых абстрактных классов, которые
можно использовать для наследования или проверки типа объекта.
Большая часть классов находится в collections.abc
и часть из них показана в таблице ниже.
Полный перечень классов collections.abc
доступен в документации
ABC |
Наследует |
Абстрактные методы |
Mixin методы |
---|---|---|---|
Container |
__contains__ |
||
Hashable |
__hash__ |
||
Iterable |
__iter__ |
||
Iterator |
Iterable |
__next__ |
__iter__ |
Reversible |
Iterable |
__reversed__ |
|
Generator |
Iterator |
send, throw |
close, __iter__, __next__ |
Sized |
__len__ |
||
Callable |
__call__ |
||
Collection |
Sized, Iterable, Container |
__contains__, __iter__, __len__ |
|
Sequence |
Reversible, Collection |
__getitem__, __len__ |
__contains__, __iter__, __reversed__, index, count |
Один из вариантов использования абстрактных классов collections.abc - это проверка того поддерживает ли объект протокол. Например, проверить является ли объект итерируемым можно таким образом:
In [1]: from collections.abc import Iterable
In [2]: l1 = [1, 2, 3]
In [3]: s1 = 'line'
In [4]: n1 = 5
In [5]: isinstance(l1, Iterable)
Out[5]: True
In [6]: isinstance(s1, Iterable)
Out[6]: True
In [7]: isinstance(n1, Iterable)
Out[7]: False
Второй вариант использования классов collections.abc - наследование классов для поддержки определенного интерфейса. Например, повторим пример с классом Network из подраздела «Протокол последовательности», но теперь с наследованием класса Sequence:
In [1]: from collections.abc import Sequence
In [2]: import ipaddress
In [3]: class Network(Sequence):
...: def __init__(self, network):
...: self.network = network
...: subnet = ipaddress.ip_network(self.network)
...: self.addresses = [str(ip) for ip in subnet.hosts()]
...:
Пробуем создать экземпляр класса Network:
In [4]: net1 = Network('10.1.1.192/29')
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-4-9c2ed79a8719> in <module>
----> 1 net1 = Network('10.1.1.192/29')
TypeError: Cant instantiate abstract class Network with abstract methods __getitem__, __len__
Исключение указывает, что экземпляр не может быть создан, так как в классе Network нет методов __getitem__ и __len__. Это методы, которые созданы как абстрактные и в таблице выше указаны в соответствующем столбце. Добавляем эти методы в класс Network:
In [5]: class Network(Sequence):
...: def __init__(self, network):
...: self.network = network
...: subnet = ipaddress.ip_network(self.network)
...: self.addresses = [str(ip) for ip in subnet.hosts()]
...:
...: def __getitem__(self, index):
...: return self.addresses[index]
...:
...: def __len__(self):
...: return len(self.addresses)
...:
Теперь можно создать экземпляр класса Network и экземпляр поддерживает обращение по индексу, а также работает функция len:
In [6]: net1 = Network('10.1.1.192/29')
In [7]: net1.addresses
Out[7]:
['10.1.1.193',
'10.1.1.194',
'10.1.1.195',
'10.1.1.196',
'10.1.1.197',
'10.1.1.198']
In [8]: len(net1)
Out[8]: 6
In [9]: net1[4]
Out[9]: '10.1.1.197'
Кроме того, за счет наследования Sequence, в классе появились методы __contains__, __iter__, __reversed__, index и count:
In [10]: '10.1.1.193' in net1
Out[10]: True
In [11]: i = iter(net1)
In [12]: next(i)
Out[12]: '10.1.1.193'
In [13]: next(i)
Out[13]: '10.1.1.194'
In [14]: list(reversed(net1))
Out[14]:
['10.1.1.198',
'10.1.1.197',
'10.1.1.196',
'10.1.1.195',
'10.1.1.194',
'10.1.1.193']
In [15]: net1.index('10.1.1.195')
Out[15]: 2
In [16]: net1.count('10.1.1.197')
Out[16]: 1