Создание классов с помощью namedtuple#
collections.namedtuple#
Функция namedtuple позволяет создавать новые классы, которые наследуют tuple и при этом:
доступ к атрибутам может осуществляться по имени
доступ к элементами по индексу
экземпляр класса является итерируемым объектом
атрибуты неизменяемы
Именованные кортежи присваивают имена каждому элементу кортежа и код выглядит более понятным, так как вместо индексов используются имена. При этом, все возможности обычных кортежей остаются.
In [1]: from collections import namedtuple
In [2]: RouterClass = namedtuple('Router', ['hostname', 'ip', 'ios'])
In [3]: r1 = RouterClass('r1', '10.1.1.1', '15.4')
In [30]: r1
Out[30]: Router(hostname='r1', ip='10.1.1.1', ios='15.4')
In [18]: r1.hostname
Out[18]: 'r1'
In [19]: r1.ip
Out[19]: '10.1.1.1'
In [20]: hostname, ip, ios = r1
In [21]: hostname
Out[21]: 'r1'
In [22]: ip
Out[22]: '10.1.1.1'
In [23]: ios
Out[23]: '15.4'
Метод _as_dict возвращает OrderedDict:
In [9]: r1._asdict()
Out[9]: OrderedDict([('hostname', 'r1'), ('ip', '10.1.1.1'), ('ios', '15.4')])
Метод _replace возвращает новый экземпляр класса, в котором заменены указанные поля:
In [18]: r1 = RouterClass('r1', '10.1.1.1', '15.4')
In [19]: r1
Out[19]: Router(hostname='r1', ip='10.1.1.1', ios='15.4')
In [20]: r1._replace(ip='10.2.2.2')
Out[20]: Router(hostname='r1', ip='10.2.2.2', ios='15.4')
Метод _make создает новый экземпляр класса из последовательности полей (это метод класса):
In [22]: RouterClass._make(['r3', '10.3.3.3', '15.2'])
Out[22]: Router(hostname='r3', ip='10.3.3.3', ios='15.2')
In [23]: r3 = RouterClass._make(['r3', '10.3.3.3', '15.2'])
Пример использования namedtuple:
import sqlite3
from collections import namedtuple
key = 'vlan'
value = 10
db_filename = 'dhcp_snooping.db'
keys = ['mac', 'ip', 'vlan', 'interface', 'switch']
DhcpSnoopRecord = namedtuple('DhcpSnoopRecord', keys)
conn = sqlite3.connect(db_filename)
query = 'select {} from dhcp where {} = ?'.format(','.join(keys), key)
print('-' * 40)
for row in map(DhcpSnoopRecord._make, conn.execute(query, (value,))):
print(row.mac, row.ip, row.interface, sep='\n')
print('-' * 40)
Вывод:
$ python get_data.py
----------------------------------------
00:09:BB:3D:D6:58
10.1.10.2
FastEthernet0/1
----------------------------------------
00:07:BC:3F:A6:50
10.1.10.6
FastEthernet0/3
----------------------------------------
Параметр defaults позволяет указывать значения по умолчанию:
In [33]: IPAddress = namedtuple('IPAddress', ['address', 'mask'], defaults=[24])
In [34]: ip1 = IPAddress('10.1.1.1', 28)
In [35]: ip1
Out[35]: IPAddress(address='10.1.1.1', mask=28)
In [36]: ip2 = IPAddress('10.2.2.2')
In [37]: ip2
Out[37]: IPAddress(address='10.2.2.2', mask=24)
typing.NamedTuple#
Еще один вариант создания класса с помощью именнованного кортежа - наследование класса typing.NamedTuple. Базовые особенности namedtuple сохраняются, плюс есть возможность добавлять свои методы.
In [9]: from typing import NamedTuple
In [10]: class IPAddress(typing.NamedTuple):
...: ip: str
...: mask: int = 24
...:
In [11]: ip1 = IPAddress('10.1.1.1', 28)
In [12]: ip1
Out[12]: IPAddress(ip='10.1.1.1', mask=28)
In [13]: class IPAddress(typing.NamedTuple):
...: ip: str
...: mask: int = 24
...:
...: def convert_to_bin(self):
...: pass
...:
In [14]: ip1 = IPAddress('10.1.1.1', 28)
In [15]: ip1.convert_to_bin
Out[15]: <bound method IPAddress.convert_to_bin of IPAddress(ip='10.1.1.1', mask=28)>